mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-05 06:50:04 +00:00
Groups, Group Nano powers and Revive (#129)
* Initial Group Implementation * Request/refuse/join and leave groups. * Chat into groups. * Get status updates on every group member each tick. * Owner leaving the group destroys the entire group. * Added more nano powers * Revive for both variants work. * Many nano powers now have a group variant working. * Enemy checks for aggro before retreating. * Enemies keep aggro on dead players with revive nanos out. * Further Nano powers + Bugfixes * Infection damage now relies on bitcondition flags. * Antidote power now works. * Improved how groups handle leaving players. * Fixed mob aggro range. * Group Healing is now functional. * Possibly fixed the player being unselectable bug. * Fixed indentations. * Dismiss nano when starting a MSS ride * Sneak, Invisibility and Bugfixes * Sneak and invisibility affect mob aggro. * Possibly bugfixed equips not showing to other players. * Aggro checking is less likely to cause nullptr related crashes. * Group PR cleanup. * Made sure to label all hacky workarounds * Implemented the Antidote nano power the right way * Cleaned up the way various little things are written Didn't have the opportunity to actually test groups. Co-authored-by: CakeLancelot <CakeLancelot@users.noreply.github.com> Co-authored-by: CPunch <sethtstubbs@gmail.com> Co-authored-by: dongresource <dongresource@protonmail.com>
This commit is contained in:
parent
131eb94919
commit
b8f586bc10
2
Makefile
2
Makefile
@ -49,6 +49,7 @@ CXXSRC=\
|
|||||||
src/TableData.cpp\
|
src/TableData.cpp\
|
||||||
src/ChunkManager.cpp\
|
src/ChunkManager.cpp\
|
||||||
src/BuddyManager.cpp\
|
src/BuddyManager.cpp\
|
||||||
|
src/GroupManager.cpp\
|
||||||
|
|
||||||
# headers (for timestamp purposes)
|
# headers (for timestamp purposes)
|
||||||
CHDR=\
|
CHDR=\
|
||||||
@ -86,6 +87,7 @@ CXXHDR=\
|
|||||||
src/TableData.hpp\
|
src/TableData.hpp\
|
||||||
src/ChunkManager.hpp\
|
src/ChunkManager.hpp\
|
||||||
src/BuddyManager.hpp\
|
src/BuddyManager.hpp\
|
||||||
|
src/GroupManager.hpp\
|
||||||
|
|
||||||
COBJ=$(CSRC:.c=.o)
|
COBJ=$(CSRC:.c=.o)
|
||||||
CXXOBJ=$(CXXSRC:.cpp=.o)
|
CXXOBJ=$(CXXSRC:.cpp=.o)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Database.hpp"
|
#include "Database.hpp"
|
||||||
|
#include "Database.hpp"
|
||||||
#include "contrib/bcrypt/BCrypt.hpp"
|
#include "contrib/bcrypt/BCrypt.hpp"
|
||||||
#include "CNProtocol.hpp"
|
#include "CNProtocol.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -456,7 +457,6 @@ Player Database::DbToPlayer(DbPlayer player) {
|
|||||||
result.equippedNanos[1] = player.Nano2;
|
result.equippedNanos[1] = player.Nano2;
|
||||||
result.equippedNanos[2] = player.Nano3;
|
result.equippedNanos[2] = player.Nano3;
|
||||||
|
|
||||||
result.dotDamage = false;
|
|
||||||
result.inCombat = false;
|
result.inCombat = false;
|
||||||
|
|
||||||
result.iWarpLocationFlag = player.WarpLocationFlag;
|
result.iWarpLocationFlag = player.WarpLocationFlag;
|
||||||
|
336
src/GroupManager.cpp
Normal file
336
src/GroupManager.cpp
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
#include "CNShardServer.hpp"
|
||||||
|
#include "CNStructs.hpp"
|
||||||
|
#include "ChatManager.hpp"
|
||||||
|
#include "PlayerManager.hpp"
|
||||||
|
#include "GroupManager.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
void GroupManager::init() {
|
||||||
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE, requestGroup);
|
||||||
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE_REFUSE, refuseGroup);
|
||||||
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_JOIN, joinGroup);
|
||||||
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_LEAVE, leaveGroup);
|
||||||
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE, chatGroup);
|
||||||
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE, menuChatGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::requestGroup(CNSocket* sock, CNPacketData* data) {
|
||||||
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_GROUP_INVITE))
|
||||||
|
return; // malformed packet
|
||||||
|
|
||||||
|
sP_CL2FE_REQ_PC_GROUP_INVITE* recv = (sP_CL2FE_REQ_PC_GROUP_INVITE*)data->buf;
|
||||||
|
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player* otherPlr = PlayerManager::getPlayerFromID(recv->iID_To);
|
||||||
|
|
||||||
|
if (plr == nullptr || otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
otherPlr = PlayerManager::getPlayerFromID(otherPlr->iIDGroup);
|
||||||
|
|
||||||
|
if (otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// fail if the group is full or the other player is already in a group
|
||||||
|
if (plr->groupCnt >= 4 || otherPlr->groupCnt > 1) {
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_GROUP_INVITE_FAIL, resp);
|
||||||
|
sock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE_FAIL, sizeof(sP_FE2CL_PC_GROUP_INVITE_FAIL));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CNSocket* otherSock = PlayerManager::getSockFromID(recv->iID_To);
|
||||||
|
|
||||||
|
if (otherSock == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_GROUP_INVITE, resp);
|
||||||
|
|
||||||
|
resp.iHostID = plr->iIDGroup;
|
||||||
|
|
||||||
|
otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE, sizeof(sP_FE2CL_PC_GROUP_INVITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::refuseGroup(CNSocket* sock, CNPacketData* data) {
|
||||||
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_GROUP_INVITE_REFUSE))
|
||||||
|
return; // malformed packet
|
||||||
|
|
||||||
|
sP_CL2FE_REQ_PC_GROUP_INVITE_REFUSE* recv = (sP_CL2FE_REQ_PC_GROUP_INVITE_REFUSE*)data->buf;
|
||||||
|
|
||||||
|
CNSocket* otherSock = PlayerManager::getSockFromID(recv->iID_From);
|
||||||
|
|
||||||
|
if (otherSock == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
|
if (plr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_GROUP_INVITE_REFUSE, resp);
|
||||||
|
|
||||||
|
resp.iID_To = plr->iID;
|
||||||
|
|
||||||
|
otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE_REFUSE, sizeof(sP_FE2CL_PC_GROUP_INVITE_REFUSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::joinGroup(CNSocket* sock, CNPacketData* data) {
|
||||||
|
if (data->size != sizeof(sP_CL2FE_REQ_PC_GROUP_JOIN))
|
||||||
|
return; // malformed packet
|
||||||
|
|
||||||
|
sP_CL2FE_REQ_PC_GROUP_JOIN* recv = (sP_CL2FE_REQ_PC_GROUP_JOIN*)data->buf;
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player* otherPlr = PlayerManager::getPlayerFromID(recv->iID_From);
|
||||||
|
|
||||||
|
if (plr == nullptr || otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
otherPlr = PlayerManager::getPlayerFromID(otherPlr->iIDGroup);
|
||||||
|
|
||||||
|
if (otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// fail if the group is full the other player is already in a group
|
||||||
|
if (plr->groupCnt > 1 || plr->iIDGroup != plr->iID || otherPlr->groupCnt >= 4) {
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_GROUP_INVITE_FAIL, resp);
|
||||||
|
sock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_JOIN_FAIL, sizeof(sP_FE2CL_PC_GROUP_JOIN_FAIL));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_JOIN), otherPlr->groupCnt + 1, sizeof(sPCGroupMemberInfo))) {
|
||||||
|
std::cout << "[WARN] bad sP_FE2CL_PC_GROUP_JOIN packet size\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plr->iIDGroup = otherPlr->iID;
|
||||||
|
otherPlr->groupCnt += 1;
|
||||||
|
otherPlr->groupIDs[otherPlr->groupCnt-1] = plr->iID;
|
||||||
|
|
||||||
|
size_t resplen = sizeof(sP_FE2CL_PC_GROUP_JOIN) + otherPlr->groupCnt * sizeof(sPCGroupMemberInfo);
|
||||||
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
|
||||||
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_PC_GROUP_JOIN *resp = (sP_FE2CL_PC_GROUP_JOIN*)respbuf;
|
||||||
|
sPCGroupMemberInfo *respdata = (sPCGroupMemberInfo*)(respbuf+sizeof(sP_FE2CL_PC_GROUP_JOIN));
|
||||||
|
|
||||||
|
resp->iID_NewMember = plr->iID;
|
||||||
|
resp->iMemberPCCnt = otherPlr->groupCnt;
|
||||||
|
|
||||||
|
for (int i = 0; i < otherPlr->groupCnt; i++) {
|
||||||
|
Player* varPlr = PlayerManager::getPlayerFromID(otherPlr->groupIDs[i]);
|
||||||
|
|
||||||
|
if (varPlr == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
respdata[i].iPC_ID = varPlr->iID;
|
||||||
|
respdata[i].iPCUID = varPlr->PCStyle.iPC_UID;
|
||||||
|
respdata[i].iNameCheck = varPlr->PCStyle.iNameCheck;
|
||||||
|
memcpy(respdata[i].szFirstName, varPlr->PCStyle.szFirstName, sizeof(varPlr->PCStyle.szFirstName));
|
||||||
|
memcpy(respdata[i].szLastName, varPlr->PCStyle.szLastName, sizeof(varPlr->PCStyle.szLastName));
|
||||||
|
respdata[i].iSpecialState = varPlr->iSpecialState;
|
||||||
|
respdata[i].iLv = varPlr->level;
|
||||||
|
respdata[i].iHP = varPlr->HP;
|
||||||
|
respdata[i].iMaxHP = PC_MAXHEALTH(varPlr->level);
|
||||||
|
//respdata[i].iMapType = 0;
|
||||||
|
//respdata[i].iMapNum = 0;
|
||||||
|
respdata[i].iX = varPlr->x;
|
||||||
|
respdata[i].iY = varPlr->y;
|
||||||
|
respdata[i].iZ = varPlr->z;
|
||||||
|
// client doesnt read nano data here
|
||||||
|
}
|
||||||
|
|
||||||
|
sendToGroup(otherPlr, (void*)&respbuf, P_FE2CL_PC_GROUP_JOIN, resplen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::leaveGroup(CNSocket* sock, CNPacketData* data) {
|
||||||
|
if (data->size != sizeof(P_CL2FE_REQ_PC_GROUP_LEAVE))
|
||||||
|
return; // malformed packet
|
||||||
|
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
|
if (plr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
groupKickPlayer(plr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::chatGroup(CNSocket* sock, CNPacketData* data) {
|
||||||
|
if (data->size != sizeof(sP_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE))
|
||||||
|
return; // malformed packet
|
||||||
|
|
||||||
|
sP_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_ALL_GROUP_FREECHAT_MESSAGE*)data->buf;
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (plr == nullptr || otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// send to client
|
||||||
|
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp);
|
||||||
|
memcpy(resp.szFreeChat, chat->szFreeChat, sizeof(chat->szFreeChat));
|
||||||
|
resp.iSendPCID = plr->iID;
|
||||||
|
resp.iEmoteCode = chat->iEmoteCode;
|
||||||
|
sendToGroup(otherPlr, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::menuChatGroup(CNSocket* sock, CNPacketData* data) {
|
||||||
|
if (data->size != sizeof(sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE))
|
||||||
|
return; // malformed packet
|
||||||
|
|
||||||
|
sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE*)data->buf;
|
||||||
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (plr == nullptr || otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// send to client
|
||||||
|
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp);
|
||||||
|
memcpy(resp.szFreeChat, chat->szFreeChat, sizeof(chat->szFreeChat));
|
||||||
|
resp.iSendPCID = plr->iID;
|
||||||
|
resp.iEmoteCode = chat->iEmoteCode;
|
||||||
|
sendToGroup(otherPlr, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::sendToGroup(Player* plr, void* buf, uint32_t type, size_t size) {
|
||||||
|
for (int i = 0; i < plr->groupCnt; i++) {
|
||||||
|
CNSocket* sock = PlayerManager::getSockFromID(plr->groupIDs[i]);
|
||||||
|
|
||||||
|
if (sock == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (type == P_FE2CL_PC_GROUP_LEAVE_SUCC) {
|
||||||
|
Player* leavingPlr = PlayerManager::getPlayer(sock);
|
||||||
|
leavingPlr->iIDGroup = leavingPlr->iID;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock->sendPacket(buf, type, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::groupTickInfo(Player* plr) {
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO), plr->groupCnt, sizeof(sPCGroupMemberInfo))) {
|
||||||
|
std::cout << "[WARN] bad sP_FE2CL_PC_GROUP_JOIN packet size\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t resplen = sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO) + plr->groupCnt * sizeof(sPCGroupMemberInfo);
|
||||||
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
|
||||||
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_PC_GROUP_MEMBER_INFO *resp = (sP_FE2CL_PC_GROUP_MEMBER_INFO*)respbuf;
|
||||||
|
sPCGroupMemberInfo *respdata = (sPCGroupMemberInfo*)(respbuf+sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO));
|
||||||
|
|
||||||
|
resp->iID = plr->iID;
|
||||||
|
resp->iMemberPCCnt = plr->groupCnt;
|
||||||
|
|
||||||
|
for (int i = 0; i < plr->groupCnt; i++) {
|
||||||
|
Player* varPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]);
|
||||||
|
|
||||||
|
if (varPlr == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
respdata[i].iPC_ID = varPlr->iID;
|
||||||
|
respdata[i].iPCUID = varPlr->PCStyle.iPC_UID;
|
||||||
|
respdata[i].iNameCheck = varPlr->PCStyle.iNameCheck;
|
||||||
|
memcpy(respdata[i].szFirstName, varPlr->PCStyle.szFirstName, sizeof(varPlr->PCStyle.szFirstName));
|
||||||
|
memcpy(respdata[i].szLastName, varPlr->PCStyle.szLastName, sizeof(varPlr->PCStyle.szLastName));
|
||||||
|
respdata[i].iSpecialState = varPlr->iSpecialState;
|
||||||
|
respdata[i].iLv = varPlr->level;
|
||||||
|
respdata[i].iHP = varPlr->HP;
|
||||||
|
respdata[i].iMaxHP = PC_MAXHEALTH(varPlr->level);
|
||||||
|
//respdata[i].iMapType = 0;
|
||||||
|
//respdata[i].iMapNum = 0;
|
||||||
|
respdata[i].iX = varPlr->x;
|
||||||
|
respdata[i].iY = varPlr->y;
|
||||||
|
respdata[i].iZ = varPlr->z;
|
||||||
|
if (varPlr->activeNano > 0) {
|
||||||
|
respdata[i].bNano = 1;
|
||||||
|
respdata[i].Nano = varPlr->Nanos[varPlr->activeNano];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendToGroup(plr, (void*)&respbuf, P_FE2CL_PC_GROUP_MEMBER_INFO, resplen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupManager::groupKickPlayer(Player* plr) {
|
||||||
|
// if you are the group leader, destroy your own group and kick everybody
|
||||||
|
if (plr->iID == plr->iIDGroup) {
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_GROUP_LEAVE_SUCC, resp1);
|
||||||
|
sendToGroup(plr, (void*)&resp1, P_FE2CL_PC_GROUP_LEAVE_SUCC, sizeof(sP_FE2CL_PC_GROUP_LEAVE_SUCC));
|
||||||
|
plr->groupCnt = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_LEAVE), otherPlr->groupCnt - 1, sizeof(sPCGroupMemberInfo))) {
|
||||||
|
std::cout << "[WARN] bad sP_FE2CL_PC_GROUP_LEAVE packet size\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t resplen = sizeof(sP_FE2CL_PC_GROUP_LEAVE) + (otherPlr->groupCnt - 1) * sizeof(sPCGroupMemberInfo);
|
||||||
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
|
||||||
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_PC_GROUP_LEAVE *resp = (sP_FE2CL_PC_GROUP_LEAVE*)respbuf;
|
||||||
|
sPCGroupMemberInfo *respdata = (sPCGroupMemberInfo*)(respbuf+sizeof(sP_FE2CL_PC_GROUP_LEAVE));
|
||||||
|
|
||||||
|
resp->iID_LeaveMember = plr->iID;
|
||||||
|
resp->iMemberPCCnt = otherPlr->groupCnt - 1;
|
||||||
|
|
||||||
|
int moveDown = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < otherPlr->groupCnt; i++) {
|
||||||
|
Player* varPlr = PlayerManager::getPlayerFromID(otherPlr->groupIDs[i]);
|
||||||
|
|
||||||
|
if (varPlr == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (moveDown == 1)
|
||||||
|
otherPlr->groupIDs[i-1] = otherPlr->groupIDs[i];
|
||||||
|
|
||||||
|
respdata[i-moveDown].iPC_ID = varPlr->iID;
|
||||||
|
respdata[i-moveDown].iPCUID = varPlr->PCStyle.iPC_UID;
|
||||||
|
respdata[i-moveDown].iNameCheck = varPlr->PCStyle.iNameCheck;
|
||||||
|
memcpy(respdata[i-moveDown].szFirstName, varPlr->PCStyle.szFirstName, sizeof(varPlr->PCStyle.szFirstName));
|
||||||
|
memcpy(respdata[i-moveDown].szLastName, varPlr->PCStyle.szLastName, sizeof(varPlr->PCStyle.szLastName));
|
||||||
|
respdata[i-moveDown].iSpecialState = varPlr->iSpecialState;
|
||||||
|
respdata[i-moveDown].iLv = varPlr->level;
|
||||||
|
respdata[i-moveDown].iHP = varPlr->HP;
|
||||||
|
respdata[i-moveDown].iMaxHP = PC_MAXHEALTH(varPlr->level);
|
||||||
|
//respdata[i-moveDown]].iMapType = 0;
|
||||||
|
//respdata[i-moveDown]].iMapNum = 0;
|
||||||
|
respdata[i-moveDown].iX = varPlr->x;
|
||||||
|
respdata[i-moveDown].iY = varPlr->y;
|
||||||
|
respdata[i-moveDown].iZ = varPlr->z;
|
||||||
|
// client doesnt read nano data here
|
||||||
|
|
||||||
|
if (varPlr == plr) {
|
||||||
|
moveDown = 1;
|
||||||
|
otherPlr->groupIDs[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plr->iIDGroup = plr->iID;
|
||||||
|
otherPlr->groupCnt -= 1;
|
||||||
|
|
||||||
|
sendToGroup(otherPlr, (void*)&respbuf, P_FE2CL_PC_GROUP_LEAVE, resplen);
|
||||||
|
|
||||||
|
CNSocket* sock = PlayerManager::getSockFromID(plr->iID);
|
||||||
|
|
||||||
|
if (sock == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_GROUP_LEAVE_SUCC, resp1);
|
||||||
|
sock->sendPacket((void*)&resp1, P_FE2CL_PC_GROUP_LEAVE_SUCC, sizeof(sP_FE2CL_PC_GROUP_LEAVE_SUCC));
|
||||||
|
}
|
23
src/GroupManager.hpp
Normal file
23
src/GroupManager.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Player.hpp"
|
||||||
|
#include "CNProtocol.hpp"
|
||||||
|
#include "CNStructs.hpp"
|
||||||
|
#include "CNShardServer.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace GroupManager {
|
||||||
|
void init();
|
||||||
|
|
||||||
|
void requestGroup(CNSocket* sock, CNPacketData* data);
|
||||||
|
void refuseGroup(CNSocket* sock, CNPacketData* data);
|
||||||
|
void joinGroup(CNSocket* sock, CNPacketData* data);
|
||||||
|
void leaveGroup(CNSocket* sock, CNPacketData* data);
|
||||||
|
void chatGroup(CNSocket* sock, CNPacketData* data);
|
||||||
|
void menuChatGroup(CNSocket* sock, CNPacketData* data);
|
||||||
|
void sendToGroup(Player* plr, void* buf, uint32_t type, size_t size);
|
||||||
|
void groupTickInfo(Player* plr);
|
||||||
|
void groupKickPlayer(Player* plr);
|
||||||
|
}
|
@ -911,3 +911,16 @@ void ItemManager::setItemStats(Player* plr) {
|
|||||||
plr->defense += itemStatsDat->defense;
|
plr->defense += itemStatsDat->defense;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: work around the invisible weapon bug
|
||||||
|
void ItemManager::updateEquips(CNSocket* sock, Player* plr) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, resp);
|
||||||
|
|
||||||
|
resp.iPC_ID = plr->iID;
|
||||||
|
resp.iEquipSlotNum = i;
|
||||||
|
resp.EquipSlotItem = plr->Equip[i];
|
||||||
|
|
||||||
|
PlayerManager::sendToViewable(sock, (void*)&resp, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -50,4 +50,5 @@ namespace ItemManager {
|
|||||||
Item* getItemData(int32_t id, int32_t type);
|
Item* getItemData(int32_t id, int32_t type);
|
||||||
void checkItemExpire(CNSocket* sock, Player* player);
|
void checkItemExpire(CNSocket* sock, Player* player);
|
||||||
void setItemStats(Player* plr);
|
void setItemStats(Player* plr);
|
||||||
|
void updateEquips(CNSocket* sock, Player* plr);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "NPCManager.hpp"
|
#include "NPCManager.hpp"
|
||||||
#include "ItemManager.hpp"
|
#include "ItemManager.hpp"
|
||||||
#include "MissionManager.hpp"
|
#include "MissionManager.hpp"
|
||||||
|
#include "GroupManager.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -95,7 +96,7 @@ void MobManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
|||||||
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_NPCs, resplen);
|
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_NPCs, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MobManager::npcAttackPc(Mob *mob) {
|
void MobManager::npcAttackPc(Mob *mob, time_t currTime) {
|
||||||
Player *plr = PlayerManager::getPlayer(mob->target);
|
Player *plr = PlayerManager::getPlayer(mob->target);
|
||||||
|
|
||||||
if (plr == nullptr)
|
if (plr == nullptr)
|
||||||
@ -126,6 +127,7 @@ void MobManager::npcAttackPc(Mob *mob) {
|
|||||||
if (plr->HP <= 0) {
|
if (plr->HP <= 0) {
|
||||||
mob->target = nullptr;
|
mob->target = nullptr;
|
||||||
mob->state = MobState::RETREAT;
|
mob->state = MobState::RETREAT;
|
||||||
|
aggroCheck(mob, currTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,8 +269,10 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
if (PlayerManager::players.find(mob->target) == PlayerManager::players.end()) {
|
if (PlayerManager::players.find(mob->target) == PlayerManager::players.end()) {
|
||||||
mob->target = nullptr;
|
mob->target = nullptr;
|
||||||
mob->state = MobState::RETREAT;
|
mob->state = MobState::RETREAT;
|
||||||
|
aggroCheck(mob, currTime);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player *plr = PlayerManager::getPlayer(mob->target);
|
Player *plr = PlayerManager::getPlayer(mob->target);
|
||||||
|
|
||||||
if (plr == nullptr)
|
if (plr == nullptr)
|
||||||
@ -278,6 +282,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
if (plr->HP <= 0) {
|
if (plr->HP <= 0) {
|
||||||
mob->target = nullptr;
|
mob->target = nullptr;
|
||||||
mob->state = MobState::RETREAT;
|
mob->state = MobState::RETREAT;
|
||||||
|
aggroCheck(mob, currTime);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,10 +296,10 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
// attack logic
|
// attack logic
|
||||||
if (mob->nextAttack == 0) {
|
if (mob->nextAttack == 0) {
|
||||||
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; // I *think* this is what this is
|
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; // I *think* this is what this is
|
||||||
npcAttackPc(mob);
|
npcAttackPc(mob, currTime);
|
||||||
} else if (mob->nextAttack != 0 && currTime >= mob->nextAttack) {
|
} else if (mob->nextAttack != 0 && currTime >= mob->nextAttack) {
|
||||||
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
||||||
npcAttackPc(mob);
|
npcAttackPc(mob, currTime);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// movement logic
|
// movement logic
|
||||||
@ -344,32 +349,10 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
|||||||
* do so more often than if we waited for nextMovement (which is way too slow).
|
* do so more often than if we waited for nextMovement (which is way too slow).
|
||||||
*/
|
*/
|
||||||
if (mob->nextAttack == 0 || currTime >= mob->nextAttack) {
|
if (mob->nextAttack == 0 || currTime >= mob->nextAttack) {
|
||||||
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
mob->nextAttack = currTime + 500;
|
||||||
|
if (aggroCheck(mob, currTime))
|
||||||
/*
|
|
||||||
* Aggro on nearby players.
|
|
||||||
* Even if they're in range, we can't assume they're all in the same one chunk
|
|
||||||
* as the mob, since it might be near a chunk boundary.
|
|
||||||
*/
|
|
||||||
for (Chunk *chunk : mob->currentChunks) {
|
|
||||||
for (CNSocket *s : chunk->players) {
|
|
||||||
Player *plr = s->plr;
|
|
||||||
|
|
||||||
// height is relevant for aggro distance because of platforming
|
|
||||||
int xyDistance = hypot(mob->appearanceData.iX - plr->x, mob->appearanceData.iY - plr->y);
|
|
||||||
int distance = hypot(xyDistance, mob->appearanceData.iZ - plr->z);
|
|
||||||
if (distance > mob->data["m_iSightRange"])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// found player. engage.
|
|
||||||
mob->target = s;
|
|
||||||
mob->state = MobState::COMBAT;
|
|
||||||
mob->nextMovement = currTime;
|
|
||||||
mob->nextAttack = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// some mobs don't move (and we mustn't divide/modulus by zero)
|
// some mobs don't move (and we mustn't divide/modulus by zero)
|
||||||
if (mob->idleRange == 0)
|
if (mob->idleRange == 0)
|
||||||
@ -439,15 +422,11 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
|||||||
mob->nextAttack = 0;
|
mob->nextAttack = 0;
|
||||||
mob->appearanceData.iConditionBitFlag = 0;
|
mob->appearanceData.iConditionBitFlag = 0;
|
||||||
|
|
||||||
//INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData);
|
|
||||||
//enterData.NPCAppearanceData = mob->appearanceData;
|
|
||||||
//NPCManager::sendToViewable(mob, &enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER));
|
|
||||||
resendMobHP(mob);
|
resendMobHP(mob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MobManager::step(CNServer *serv, time_t currTime) {
|
void MobManager::step(CNServer *serv, time_t currTime) {
|
||||||
|
|
||||||
for (auto& pair : Mobs) {
|
for (auto& pair : Mobs) {
|
||||||
int x = pair.second->appearanceData.iX;
|
int x = pair.second->appearanceData.iX;
|
||||||
int y = pair.second->appearanceData.iY;
|
int y = pair.second->appearanceData.iY;
|
||||||
@ -525,8 +504,21 @@ std::pair<int,int> MobManager::lerp(int x1, int y1, int x2, int y2, int speed) {
|
|||||||
void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {
|
void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
if (plr != nullptr)
|
if (plr == nullptr) {
|
||||||
|
std::cout << "[WARN] combatBegin: null player!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
plr->inCombat = true;
|
plr->inCombat = true;
|
||||||
|
|
||||||
|
// HACK: make sure the player has the right weapon out for combat
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, resp);
|
||||||
|
|
||||||
|
resp.iPC_ID = plr->iID;
|
||||||
|
resp.iEquipSlotNum = 0;
|
||||||
|
resp.EquipSlotItem = plr->Equip[0];
|
||||||
|
|
||||||
|
PlayerManager::sendToViewable(sock, (void*)&resp, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {
|
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {
|
||||||
@ -540,26 +532,58 @@ void MobManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {
|
|||||||
sP_CL2FE_DOT_DAMAGE_ONOFF *pkt = (sP_CL2FE_DOT_DAMAGE_ONOFF*)data->buf;
|
sP_CL2FE_DOT_DAMAGE_ONOFF *pkt = (sP_CL2FE_DOT_DAMAGE_ONOFF*)data->buf;
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
if (plr != nullptr)
|
if (plr == nullptr) {
|
||||||
plr->dotDamage = (bool)pkt->iFlag;
|
std::cout << "[WARN] dotDamageOnOff: null player!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((plr->iConditionBitFlag & CSB_BIT_INFECTION) != (bool)pkt->iFlag)
|
||||||
|
plr->iConditionBitFlag ^= CSB_BIT_INFECTION;
|
||||||
|
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt1);
|
||||||
|
|
||||||
|
pkt1.eCSTB = ECSB_INFECTION; //eCharStatusTimeBuffID
|
||||||
|
pkt1.eTBU = 1; //eTimeBuffUpdate
|
||||||
|
pkt1.eTBT = 0; //eTimeBuffType 1 means nano
|
||||||
|
pkt1.iConditionBitFlag = plr->iConditionBitFlag;
|
||||||
|
|
||||||
|
sock->sendPacket((void*)&pkt1, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MobManager::dealGooDamage(CNSocket *sock, int amount) {
|
void MobManager::dealGooDamage(CNSocket *sock, int amount) {
|
||||||
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_DotDamage);
|
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_DotDamage);
|
||||||
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||||
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
|
||||||
memset(respbuf, 0, resplen);
|
|
||||||
|
|
||||||
sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf;
|
|
||||||
sSkillResult_DotDamage *dmg = (sSkillResult_DotDamage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK));
|
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
if (plr == nullptr)
|
if (plr == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// update player
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf;
|
||||||
|
sSkillResult_DotDamage *dmg = (sSkillResult_DotDamage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK));
|
||||||
|
|
||||||
|
if (plr->iConditionBitFlag & CSB_BIT_PROTECT_INFECTION) {
|
||||||
|
amount = -2; // -2 is the magic number for "Protected" to appear as the damage number
|
||||||
|
dmg->bProtected = 1;
|
||||||
|
|
||||||
|
// it's hypothetically possible to have the protection bit without a nano
|
||||||
|
if (plr->activeNano != -1)
|
||||||
|
plr->Nanos[plr->activeNano].iStamina -= 3;
|
||||||
|
} else {
|
||||||
plr->HP -= amount;
|
plr->HP -= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plr->activeNano != -1) {
|
||||||
|
dmg->iStamina = plr->Nanos[plr->activeNano].iStamina;
|
||||||
|
|
||||||
|
if (plr->Nanos[plr->activeNano].iStamina <= 0) {
|
||||||
|
dmg->bNanoDeactive = 1;
|
||||||
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
|
NanoManager::summonNano(PlayerManager::getSockFromID(plr->iID), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pkt->iID = plr->iID;
|
pkt->iID = plr->iID;
|
||||||
pkt->eCT = 1; // player
|
pkt->eCT = 1; // player
|
||||||
@ -569,6 +593,7 @@ void MobManager::dealGooDamage(CNSocket *sock, int amount) {
|
|||||||
dmg->iID = plr->iID;
|
dmg->iID = plr->iID;
|
||||||
dmg->iDamage = amount;
|
dmg->iDamage = amount;
|
||||||
dmg->iHP = plr->HP;
|
dmg->iHP = plr->HP;
|
||||||
|
dmg->iConditionBitFlag = plr->iConditionBitFlag;
|
||||||
|
|
||||||
sock->sendPacket((void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
sock->sendPacket((void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
||||||
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
||||||
@ -582,12 +607,16 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) {
|
|||||||
Player *plr = pair.second.plr;
|
Player *plr = pair.second.plr;
|
||||||
bool transmit = false;
|
bool transmit = false;
|
||||||
|
|
||||||
|
// group ticks
|
||||||
|
if (plr->groupCnt > 1)
|
||||||
|
GroupManager::groupTickInfo(plr);
|
||||||
|
|
||||||
// do not tick dead players
|
// do not tick dead players
|
||||||
if (plr->HP <= 0)
|
if (plr->HP <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// fm patch/lake damage
|
// fm patch/lake damage
|
||||||
if (plr->dotDamage)
|
if (plr->iConditionBitFlag & CSB_BIT_INFECTION)
|
||||||
dealGooDamage(sock, PC_MAXHEALTH(plr->level) * 3 / 20);
|
dealGooDamage(sock, PC_MAXHEALTH(plr->level) * 3 / 20);
|
||||||
|
|
||||||
// heal
|
// heal
|
||||||
@ -605,8 +634,10 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) {
|
|||||||
if (plr->passiveNanoOut)
|
if (plr->passiveNanoOut)
|
||||||
plr->Nanos[plr->activeNano].iStamina -= 1;
|
plr->Nanos[plr->activeNano].iStamina -= 1;
|
||||||
|
|
||||||
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
if (plr->Nanos[plr->activeNano].iStamina <= 0) {
|
||||||
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
NanoManager::summonNano(PlayerManager::getSockFromID(plr->iID), -1);
|
NanoManager::summonNano(PlayerManager::getSockFromID(plr->iID), -1);
|
||||||
|
}
|
||||||
|
|
||||||
transmit = true;
|
transmit = true;
|
||||||
} else if (plr->Nanos[plr->equippedNanos[i]].iStamina < 150) { // regain stamina
|
} else if (plr->Nanos[plr->equippedNanos[i]].iStamina < 150) { // regain stamina
|
||||||
@ -754,6 +785,7 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) {
|
|||||||
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_CHARs, resplen);
|
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_CHARs, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: we haven't found a better way to refresh a mob's client-side status
|
||||||
void MobManager::resendMobHP(Mob *mob) {
|
void MobManager::resendMobHP(Mob *mob) {
|
||||||
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Heal_HP);
|
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Heal_HP);
|
||||||
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||||
@ -775,3 +807,43 @@ void MobManager::resendMobHP(Mob *mob) {
|
|||||||
|
|
||||||
NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aggro on nearby players.
|
||||||
|
* Even if they're in range, we can't assume they're all in the same one chunk
|
||||||
|
* as the mob, since it might be near a chunk boundary.
|
||||||
|
*/
|
||||||
|
bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
|
||||||
|
for (Chunk *chunk : mob->currentChunks) {
|
||||||
|
for (CNSocket *s : chunk->players) {
|
||||||
|
Player *plr = s->plr;
|
||||||
|
|
||||||
|
if (plr->HP <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int mobRange = mob->data["m_iSightRange"];
|
||||||
|
|
||||||
|
if (plr->iConditionBitFlag & CSB_BIT_UP_STEALTH)
|
||||||
|
mobRange /= 3;
|
||||||
|
|
||||||
|
if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVISIBLE)
|
||||||
|
mobRange = -1;
|
||||||
|
|
||||||
|
// height is relevant for aggro distance because of platforming
|
||||||
|
int xyDistance = hypot(mob->appearanceData.iX - plr->x, mob->appearanceData.iY - plr->y);
|
||||||
|
int distance = hypot(xyDistance, mob->appearanceData.iZ - plr->z);
|
||||||
|
|
||||||
|
if (distance > mobRange)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// found player. engage.
|
||||||
|
mob->target = s;
|
||||||
|
mob->state = MobState::COMBAT;
|
||||||
|
mob->nextMovement = currTime;
|
||||||
|
mob->nextAttack = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace MobManager {
|
|||||||
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
||||||
void dealGooDamage(CNSocket *sock, int amount);
|
void dealGooDamage(CNSocket *sock, int amount);
|
||||||
|
|
||||||
void npcAttackPc(Mob *mob);
|
void npcAttackPc(Mob *mob, time_t currTime);
|
||||||
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
||||||
void killMob(CNSocket *sock, Mob *mob);
|
void killMob(CNSocket *sock, Mob *mob);
|
||||||
void giveReward(CNSocket *sock);
|
void giveReward(CNSocket *sock);
|
||||||
@ -110,4 +110,5 @@ namespace MobManager {
|
|||||||
|
|
||||||
void pcAttackChars(CNSocket *sock, CNPacketData *data);
|
void pcAttackChars(CNSocket *sock, CNPacketData *data);
|
||||||
void resendMobHP(Mob *mob);
|
void resendMobHP(Mob *mob);
|
||||||
|
bool aggroCheck(Mob *mob, time_t currTime);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ namespace NanoManager {
|
|||||||
|
|
||||||
// active powers
|
// active powers
|
||||||
std::set<int> StunPowers = {1, 13, 42, 59, 78, 103};
|
std::set<int> StunPowers = {1, 13, 42, 59, 78, 103};
|
||||||
std::set<int> HealPowers = {2, 7, 12, 38, 53, 61, 82, 92, 98};
|
std::set<int> HealPowers = {7, 12, 38, 53, 92, 98};
|
||||||
|
std::set<int> GroupHealPowers = {2, 61, 82};
|
||||||
std::set<int> RecallPowers = {5, 25, 66, 69, 75, 87};
|
std::set<int> RecallPowers = {5, 25, 66, 69, 75, 87};
|
||||||
std::set<int> DrainPowers = {10, 34, 37, 56, 93, 97};
|
std::set<int> DrainPowers = {10, 34, 37, 56, 93, 97};
|
||||||
std::set<int> SnarePowers = {17, 18, 27, 41, 43, 47, 90, 96, 106};
|
std::set<int> SnarePowers = {17, 18, 27, 41, 43, 47, 90, 96, 106};
|
||||||
@ -21,15 +22,19 @@ std::set<int> SleepPowers = {28, 30, 32, 49, 70, 71, 81, 85, 94};
|
|||||||
|
|
||||||
// passive powers
|
// passive powers
|
||||||
std::set<int> ScavangePowers = {3, 50, 99};
|
std::set<int> ScavangePowers = {3, 50, 99};
|
||||||
std::set<int> RunPowers = {4, 8, 62, 68, 73, 86};
|
std::set<int> RunPowers = {4, 68, 86};
|
||||||
|
std::set<int> GroupRunPowers = {8, 62, 73};
|
||||||
std::set<int> BonusPowers = {6, 54, 104};
|
std::set<int> BonusPowers = {6, 54, 104};
|
||||||
std::set<int> GuardPowers = {9, 57, 76};
|
std::set<int> GuardPowers = {9, 57, 76};
|
||||||
std::set<int> RadarPowers = {11, 67, 95};
|
std::set<int> RadarPowers = {11, 67, 95};
|
||||||
std::set<int> AntidotePowers = {14, 58, 102};
|
std::set<int> AntidotePowers = {14, 58, 102};
|
||||||
std::set<int> FreedomPowers = {15, 31, 39, 55, 77, 107};
|
std::set<int> FreedomPowers = {31, 39, 107};
|
||||||
std::set<int> JumpPowers = {16, 35, 44, 60, 88, 100};
|
std::set<int> GroupFreedomPowers = {15, 55, 77};
|
||||||
|
std::set<int> JumpPowers = {16, 44, 88};
|
||||||
|
std::set<int> GroupJumpPowers = {35, 60, 100};
|
||||||
std::set<int> SelfRevivePowers = {22, 48, 83};
|
std::set<int> SelfRevivePowers = {22, 48, 83};
|
||||||
std::set<int> SneakPowers = {23, 29, 65, 72, 80, 82};
|
std::set<int> SneakPowers = {29, 72, 80};
|
||||||
|
std::set<int> GroupSneakPowers = {23, 65, 84};
|
||||||
std::set<int> TreasureFinderPowers = {26, 40, 74};
|
std::set<int> TreasureFinderPowers = {26, 40, 74};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -148,13 +153,32 @@ void NanoManager::nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
int16_t nanoId = plr->activeNano;
|
int16_t nanoId = plr->activeNano;
|
||||||
int16_t skillId = plr->Nanos[nanoId].iSkillID;
|
int16_t skillId = plr->Nanos[nanoId].iSkillID;
|
||||||
|
|
||||||
|
DEBUGLOG(
|
||||||
|
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " requested to summon nano skill " << std::endl;
|
||||||
|
)
|
||||||
|
|
||||||
for (auto& pwr : ActivePowers)
|
for (auto& pwr : ActivePowers)
|
||||||
if (pwr.powers.count(skillId)) // std::set's contains method is C++20 only...
|
if (pwr.powers.count(skillId)) // std::set's contains method is C++20 only...
|
||||||
pwr.handle(sock, data, nanoId, skillId);
|
pwr.handle(sock, data, nanoId, skillId);
|
||||||
|
|
||||||
DEBUGLOG(
|
// Group Revive is handled separately (XXX: move into table?)
|
||||||
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " requested to summon nano skill " << std::endl;
|
if (GroupRevivePowers.find(skillId) == GroupRevivePowers.end())
|
||||||
)
|
return;
|
||||||
|
|
||||||
|
Player *leader = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (leader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < leader->groupCnt; i++) {
|
||||||
|
Player* varPlr = PlayerManager::getPlayerFromID(leader->groupIDs[i]);
|
||||||
|
|
||||||
|
if (varPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (varPlr->HP <= 0)
|
||||||
|
revivePlayer(varPlr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NanoManager::nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
|
void NanoManager::nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
|
||||||
@ -285,7 +309,7 @@ void NanoManager::summonNano(CNSocket *sock, int slot) {
|
|||||||
if (plr->activeNano > 0)
|
if (plr->activeNano > 0)
|
||||||
for (auto& pwr : PassivePowers)
|
for (auto& pwr : PassivePowers)
|
||||||
if (pwr.powers.count(plr->Nanos[plr->activeNano].iSkillID)) { // std::set's contains method is C++20 only...
|
if (pwr.powers.count(plr->Nanos[plr->activeNano].iSkillID)) { // std::set's contains method is C++20 only...
|
||||||
nanoUnbuff(sock, pwr.iCBFlag, pwr.eCharStatusTimeBuffID, pwr.iValue);
|
nanoUnbuff(sock, pwr.iCBFlag, pwr.eCharStatusTimeBuffID, pwr.iValue, pwr.groupPower);
|
||||||
plr->passiveNanoOut = false;
|
plr->passiveNanoOut = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +322,7 @@ void NanoManager::summonNano(CNSocket *sock, int slot) {
|
|||||||
for (auto& pwr : PassivePowers)
|
for (auto& pwr : PassivePowers)
|
||||||
if (pwr.powers.count(skillId)) { // std::set's contains method is C++20 only...
|
if (pwr.powers.count(skillId)) { // std::set's contains method is C++20 only...
|
||||||
resp.eCSTB___Add = 1;
|
resp.eCSTB___Add = 1;
|
||||||
nanoBuff(sock, nanoId, skillId, pwr.eSkillType, pwr.iCBFlag, pwr.eCharStatusTimeBuffID, pwr.iValue);
|
nanoBuff(sock, nanoId, skillId, pwr.eSkillType, pwr.iCBFlag, pwr.eCharStatusTimeBuffID, pwr.iValue, pwr.groupPower);
|
||||||
plr->passiveNanoOut = true;
|
plr->passiveNanoOut = true;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -444,6 +468,43 @@ bool doHeal(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *respdata, in
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool doGroupHeal(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *respdata, int i, int32_t iCBFlag, int32_t amount) {
|
||||||
|
Player *plr = nullptr;
|
||||||
|
|
||||||
|
for (auto& pair : PlayerManager::players) {
|
||||||
|
if (pair.second.plr->iID == pktdata[0]) {
|
||||||
|
plr = pair.second.plr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// player not found
|
||||||
|
if (plr == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player *leader = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
|
// player not found
|
||||||
|
if (leader == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int healedAmount = PC_MAXHEALTH(plr->level) * amount / 100;
|
||||||
|
|
||||||
|
leader->HP += healedAmount;
|
||||||
|
|
||||||
|
if (leader->HP > PC_MAXHEALTH(leader->level))
|
||||||
|
leader->HP = PC_MAXHEALTH(leader->level);
|
||||||
|
|
||||||
|
respdata[i].eCT = 1;
|
||||||
|
respdata[i].iID = plr->iID;
|
||||||
|
respdata[i].iHP = plr->HP;
|
||||||
|
respdata[i].iHealHP = healedAmount;
|
||||||
|
|
||||||
|
std::cout << (int)plr->iID << " was healed" << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool doDamage(CNSocket *sock, int32_t *pktdata, sSkillResult_Damage *respdata, int i, int32_t iCBFlag, int32_t amount) {
|
bool doDamage(CNSocket *sock, int32_t *pktdata, sSkillResult_Damage *respdata, int i, int32_t iCBFlag, int32_t amount) {
|
||||||
if (MobManager::Mobs.find(pktdata[i]) == MobManager::Mobs.end()) {
|
if (MobManager::Mobs.find(pktdata[i]) == MobManager::Mobs.end()) {
|
||||||
// not sure how to best handle this
|
// not sure how to best handle this
|
||||||
@ -521,9 +582,16 @@ bool doLeech(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *healdata, i
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: Special flags. This is still pretty dirty.
|
||||||
|
enum {
|
||||||
|
NONE,
|
||||||
|
LEECH,
|
||||||
|
GHEAL
|
||||||
|
};
|
||||||
|
|
||||||
template<class sPAYLOAD,
|
template<class sPAYLOAD,
|
||||||
bool (*work)(CNSocket*,int32_t*,sPAYLOAD*,int,int32_t,int32_t),
|
bool (*work)(CNSocket*,int32_t*,sPAYLOAD*,int,int32_t,int32_t),
|
||||||
bool isLeech=false>
|
int specialCase=NONE>
|
||||||
void activePower(CNSocket *sock, CNPacketData *data,
|
void activePower(CNSocket *sock, CNPacketData *data,
|
||||||
int16_t nanoId, int16_t skillId, int16_t eSkillType,
|
int16_t nanoId, int16_t skillId, int16_t eSkillType,
|
||||||
int32_t iCBFlag, int32_t amount) {
|
int32_t iCBFlag, int32_t amount) {
|
||||||
@ -538,20 +606,34 @@ void activePower(CNSocket *sock, CNPacketData *data,
|
|||||||
|
|
||||||
int32_t *pktdata = (int32_t*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_NANO_SKILL_USE));
|
int32_t *pktdata = (int32_t*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_NANO_SKILL_USE));
|
||||||
|
|
||||||
|
size_t resplen;
|
||||||
|
|
||||||
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player *otherPlr = plr;
|
||||||
|
|
||||||
|
if (plr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// special case since leech is atypically encoded
|
||||||
|
if constexpr (specialCase == LEECH)
|
||||||
|
resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + sizeof(sSkillResult_Heal_HP) + sizeof(sSkillResult_Damage);
|
||||||
|
else if constexpr (specialCase == GHEAL) {
|
||||||
|
otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (otherPlr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pkt->iTargetCnt = otherPlr->groupCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + pkt->iTargetCnt * sizeof(sPAYLOAD);
|
||||||
|
|
||||||
// validate response packet
|
// validate response packet
|
||||||
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), pkt->iTargetCnt, sizeof(sPAYLOAD))) {
|
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), pkt->iTargetCnt, sizeof(sPAYLOAD))) {
|
||||||
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE packet size" << std::endl;
|
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE packet size" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t resplen;
|
|
||||||
|
|
||||||
// special case since leech is atypically encoded
|
|
||||||
if constexpr (isLeech)
|
|
||||||
resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + sizeof(sSkillResult_Heal_HP) + sizeof(sSkillResult_Damage);
|
|
||||||
else
|
|
||||||
resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + pkt->iTargetCnt * sizeof(sPAYLOAD);
|
|
||||||
|
|
||||||
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
|
||||||
memset(respbuf, 0, resplen);
|
memset(respbuf, 0, resplen);
|
||||||
@ -559,12 +641,9 @@ void activePower(CNSocket *sock, CNPacketData *data,
|
|||||||
sP_FE2CL_NANO_SKILL_USE_SUCC *resp = (sP_FE2CL_NANO_SKILL_USE_SUCC*)respbuf;
|
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));
|
sPAYLOAD *respdata = (sPAYLOAD*)(respbuf+sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC));
|
||||||
|
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
|
||||||
|
|
||||||
if (plr == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
plr->Nanos[plr->activeNano].iStamina -= 40;
|
plr->Nanos[plr->activeNano].iStamina -= 40;
|
||||||
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
||||||
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
|
|
||||||
resp->iPC_ID = plr->iID;
|
resp->iPC_ID = plr->iID;
|
||||||
resp->iSkillID = skillId;
|
resp->iSkillID = skillId;
|
||||||
@ -573,8 +652,13 @@ void activePower(CNSocket *sock, CNPacketData *data,
|
|||||||
resp->eST = eSkillType;
|
resp->eST = eSkillType;
|
||||||
resp->iTargetCnt = pkt->iTargetCnt;
|
resp->iTargetCnt = pkt->iTargetCnt;
|
||||||
|
|
||||||
|
CNSocket *workSock = sock;
|
||||||
|
|
||||||
for (int i = 0; i < pkt->iTargetCnt; i++) {
|
for (int i = 0; i < pkt->iTargetCnt; i++) {
|
||||||
if (!work(sock, pktdata, respdata, i, iCBFlag, amount))
|
if constexpr (specialCase == GHEAL)
|
||||||
|
workSock = PlayerManager::getSockFromID(otherPlr->groupIDs[i]);
|
||||||
|
|
||||||
|
if (!work(workSock, pktdata, respdata, i, iCBFlag, amount))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,12 +670,12 @@ void activePower(CNSocket *sock, CNPacketData *data,
|
|||||||
std::vector<ActivePower> ActivePowers = {
|
std::vector<ActivePower> ActivePowers = {
|
||||||
ActivePower(StunPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_STUN, CSB_BIT_STUN, 0),
|
ActivePower(StunPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_STUN, CSB_BIT_STUN, 0),
|
||||||
ActivePower(HealPowers, activePower<sSkillResult_Heal_HP, doHeal>, EST_HEAL_HP, CSB_BIT_NONE, 25),
|
ActivePower(HealPowers, activePower<sSkillResult_Heal_HP, doHeal>, EST_HEAL_HP, CSB_BIT_NONE, 25),
|
||||||
|
ActivePower(GroupHealPowers, activePower<sSkillResult_Heal_HP, doGroupHeal, GHEAL>,EST_HEAL_HP, CSB_BIT_NONE, 25),
|
||||||
// TODO: Recall
|
// TODO: Recall
|
||||||
ActivePower(DrainPowers, activePower<sSkillResult_Buff, doBuff>, EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, 0),
|
ActivePower(DrainPowers, activePower<sSkillResult_Buff, doBuff>, EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, 0),
|
||||||
ActivePower(SnarePowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SNARE, CSB_BIT_DN_MOVE_SPEED, 0),
|
ActivePower(SnarePowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SNARE, CSB_BIT_DN_MOVE_SPEED, 0),
|
||||||
ActivePower(DamagePowers, activePower<sSkillResult_Damage, doDamage>, EST_DAMAGE, CSB_BIT_NONE, 12),
|
ActivePower(DamagePowers, activePower<sSkillResult_Damage, doDamage>, EST_DAMAGE, CSB_BIT_NONE, 12),
|
||||||
// TODO: GroupRevive
|
ActivePower(LeechPowers, activePower<sSkillResult_Heal_HP, doLeech, LEECH>, EST_BLOODSUCKING, CSB_BIT_NONE, 18),
|
||||||
ActivePower(LeechPowers, activePower<sSkillResult_Heal_HP, doLeech, true>, EST_BLOODSUCKING, CSB_BIT_NONE, 18),
|
|
||||||
ActivePower(SleepPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SLEEP, CSB_BIT_MEZ, 0),
|
ActivePower(SleepPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SLEEP, CSB_BIT_MEZ, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -599,18 +683,32 @@ std::vector<ActivePower> ActivePowers = {
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Passive Powers
|
#pragma region Passive Powers
|
||||||
void NanoManager::nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t eSkillType, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue) {
|
void NanoManager::nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t eSkillType, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue, bool groupPower) {
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player *leader;
|
||||||
|
|
||||||
if (plr == nullptr)
|
if (plr == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE), 1, sizeof(sSkillResult_Buff))) {
|
if (plr->iID == plr->iIDGroup)
|
||||||
|
leader = plr;
|
||||||
|
else
|
||||||
|
leader = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (leader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int pktCnt = 1;
|
||||||
|
|
||||||
|
if (groupPower)
|
||||||
|
pktCnt = leader->groupCnt;
|
||||||
|
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE), pktCnt, sizeof(sSkillResult_Buff))) {
|
||||||
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE packet size\n";
|
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE packet size\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t resplen = sizeof(sP_FE2CL_NANO_SKILL_USE) + sizeof(sSkillResult_Buff);
|
size_t resplen = sizeof(sP_FE2CL_NANO_SKILL_USE) + pktCnt * sizeof(sSkillResult_Buff);
|
||||||
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
|
||||||
memset(respbuf, 0, resplen);
|
memset(respbuf, 0, resplen);
|
||||||
@ -623,16 +721,28 @@ void NanoManager::nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t
|
|||||||
resp->iNanoID = nanoId;
|
resp->iNanoID = nanoId;
|
||||||
resp->iNanoStamina = plr->Nanos[plr->activeNano].iStamina;
|
resp->iNanoStamina = plr->Nanos[plr->activeNano].iStamina;
|
||||||
resp->eST = eSkillType;
|
resp->eST = eSkillType;
|
||||||
resp->iTargetCnt = 1;
|
resp->iTargetCnt = pktCnt;
|
||||||
|
|
||||||
// this looks stupid but in the future there will be more counts (for group powers)
|
for (int i = 0; i < pktCnt; i++) {
|
||||||
for (int i = 0; i < 1; i++) {
|
Player* varPlr;
|
||||||
|
CNSocket* sockTo;
|
||||||
|
|
||||||
if (!(plr->iConditionBitFlag & iCBFlag))
|
if (plr->iID == leader->groupIDs[i]) {
|
||||||
plr->iConditionBitFlag ^= iCBFlag;
|
varPlr = plr;
|
||||||
|
sockTo = sock;
|
||||||
|
} else {
|
||||||
|
varPlr = PlayerManager::getPlayerFromID(leader->groupIDs[i]);
|
||||||
|
sockTo = PlayerManager::getSockFromID(leader->groupIDs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varPlr == nullptr || sockTo == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(varPlr->iConditionBitFlag & iCBFlag))
|
||||||
|
varPlr->iConditionBitFlag ^= iCBFlag;
|
||||||
|
|
||||||
respdata[i].eCT = 1;
|
respdata[i].eCT = 1;
|
||||||
respdata[i].iID = plr->iID;
|
respdata[i].iID = varPlr->iID;
|
||||||
respdata[i].iConditionBitFlag = iCBFlag;
|
respdata[i].iConditionBitFlag = iCBFlag;
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt1);
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt1);
|
||||||
@ -640,38 +750,65 @@ void NanoManager::nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t
|
|||||||
pkt1.eCSTB = eCharStatusTimeBuffID; //eCharStatusTimeBuffID
|
pkt1.eCSTB = eCharStatusTimeBuffID; //eCharStatusTimeBuffID
|
||||||
pkt1.eTBU = 1; //eTimeBuffUpdate
|
pkt1.eTBU = 1; //eTimeBuffUpdate
|
||||||
pkt1.eTBT = 1; //eTimeBuffType 1 means nano
|
pkt1.eTBT = 1; //eTimeBuffType 1 means nano
|
||||||
pkt1.iConditionBitFlag = plr->iConditionBitFlag;
|
pkt1.iConditionBitFlag = varPlr->iConditionBitFlag;
|
||||||
|
|
||||||
if (iValue > 0)
|
if (iValue > 0)
|
||||||
pkt1.TimeBuff.iValue = iValue;
|
pkt1.TimeBuff.iValue = iValue;
|
||||||
|
|
||||||
sock->sendPacket((void*)&pkt1, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
sockTo->sendPacket((void*)&pkt1, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
sock->sendPacket((void*)&respbuf, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
|
sock->sendPacket((void*)&respbuf, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
|
||||||
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NANO_SKILL_USE, resplen);
|
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NANO_SKILL_USE, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NanoManager::nanoUnbuff(CNSocket* sock, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue) {
|
void NanoManager::nanoUnbuff(CNSocket* sock, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue, bool groupPower) {
|
||||||
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp1);
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, resp1);
|
||||||
|
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
Player *leader;
|
||||||
|
|
||||||
if (plr == nullptr)
|
if (plr == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (plr->iConditionBitFlag & iCBFlag)
|
if (plr->iID == plr->iIDGroup)
|
||||||
plr->iConditionBitFlag ^= iCBFlag;
|
leader = plr;
|
||||||
|
else
|
||||||
|
leader = PlayerManager::getPlayerFromID(plr->iIDGroup);
|
||||||
|
|
||||||
|
if (leader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int pktCnt = 1;
|
||||||
|
|
||||||
|
if (groupPower)
|
||||||
|
pktCnt = leader->groupCnt;
|
||||||
|
|
||||||
|
for (int i = 0; i < pktCnt; i++) {
|
||||||
|
Player* varPlr;
|
||||||
|
CNSocket* sockTo;
|
||||||
|
|
||||||
|
if (plr->iID == leader->groupIDs[i]) {
|
||||||
|
varPlr = plr;
|
||||||
|
sockTo = sock;
|
||||||
|
} else {
|
||||||
|
varPlr = PlayerManager::getPlayerFromID(leader->groupIDs[i]);
|
||||||
|
sockTo = PlayerManager::getSockFromID(leader->groupIDs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varPlr->iConditionBitFlag & iCBFlag)
|
||||||
|
varPlr->iConditionBitFlag ^= iCBFlag;
|
||||||
|
|
||||||
resp1.eCSTB = eCharStatusTimeBuffID; //eCharStatusTimeBuffID
|
resp1.eCSTB = eCharStatusTimeBuffID; //eCharStatusTimeBuffID
|
||||||
resp1.eTBU = 2; //eTimeBuffUpdate
|
resp1.eTBU = 2; //eTimeBuffUpdate
|
||||||
resp1.eTBT = 1; //eTimeBuffType 1 means nano
|
resp1.eTBT = 1; //eTimeBuffType 1 means nano
|
||||||
resp1.iConditionBitFlag = plr->iConditionBitFlag;
|
resp1.iConditionBitFlag = varPlr->iConditionBitFlag;
|
||||||
|
|
||||||
if (iValue > 0)
|
if (iValue > 0)
|
||||||
resp1.TimeBuff.iValue = iValue;
|
resp1.TimeBuff.iValue = iValue;
|
||||||
|
|
||||||
sock->sendPacket((void*)&resp1, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
sockTo->sendPacket((void*)&resp1, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0=A 1=B 2=C -1=Not found
|
// 0=A 1=B 2=C -1=Not found
|
||||||
@ -684,18 +821,63 @@ int NanoManager::nanoStyle(int nanoId) {
|
|||||||
namespace NanoManager {
|
namespace NanoManager {
|
||||||
|
|
||||||
std::vector<PassivePower> PassivePowers = {
|
std::vector<PassivePower> PassivePowers = {
|
||||||
PassivePower(ScavangePowers, EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, 0),
|
PassivePower(ScavangePowers, EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, 0, false),
|
||||||
PassivePower(RunPowers, EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, 200),
|
PassivePower(RunPowers, EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, 200, false),
|
||||||
PassivePower(BonusPowers, EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, 0),
|
PassivePower(GroupRunPowers, EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, 200, true),
|
||||||
PassivePower(GuardPowers, EST_PROTECTBATTERY, CSB_BIT_PROTECT_BATTERY, ECSB_PROTECT_BATTERY, 0),
|
PassivePower(BonusPowers, EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, 0, false),
|
||||||
PassivePower(RadarPowers, EST_MINIMAPENEMY, CSB_BIT_MINIMAP_ENEMY, ECSB_MINIMAP_ENEMY, 0),
|
PassivePower(GuardPowers, EST_PROTECTBATTERY, CSB_BIT_PROTECT_BATTERY, ECSB_PROTECT_BATTERY, 0, false),
|
||||||
PassivePower(AntidotePowers, EST_PROTECTINFECTION, CSB_BIT_PROTECT_INFECTION, ECSB_PROTECT_INFECTION, 0),
|
PassivePower(RadarPowers, EST_MINIMAPENEMY, CSB_BIT_MINIMAP_ENEMY, ECSB_MINIMAP_ENEMY, 0, false),
|
||||||
PassivePower(FreedomPowers, EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, 0),
|
PassivePower(AntidotePowers, EST_PROTECTINFECTION, CSB_BIT_PROTECT_INFECTION, ECSB_PROTECT_INFECTION, 0, false),
|
||||||
PassivePower(JumpPowers, EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, 400),
|
PassivePower(FreedomPowers, EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, 0, false),
|
||||||
PassivePower(SelfRevivePowers, EST_PHOENIX, CSB_BIT_PHOENIX, ECSB_PHOENIX, 0),
|
PassivePower(GroupFreedomPowers, EST_FREEDOM, CSB_BIT_FREEDOM, ECSB_FREEDOM, 0, true),
|
||||||
PassivePower(SneakPowers, EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, 0),
|
PassivePower(JumpPowers, EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, 400, false),
|
||||||
PassivePower(TreasureFinderPowers, EST_MINIMAPTRESURE, CSB_BIT_MINIMAP_TRESURE, ECSB_MINIMAP_TRESURE, 0),
|
PassivePower(GroupJumpPowers, EST_JUMP, CSB_BIT_UP_JUMP_HEIGHT, ECSB_UP_JUMP_HEIGHT, 400, true),
|
||||||
|
PassivePower(SelfRevivePowers, EST_PHOENIX, CSB_BIT_PHOENIX, ECSB_PHOENIX, 0, false),
|
||||||
|
PassivePower(SneakPowers, EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, 0, false),
|
||||||
|
PassivePower(GroupSneakPowers, EST_STEALTH, CSB_BIT_UP_STEALTH, ECSB_UP_STEALTH, 0, true),
|
||||||
|
PassivePower(TreasureFinderPowers, EST_MINIMAPTRESURE, CSB_BIT_MINIMAP_TRESURE, ECSB_MINIMAP_TRESURE, 0, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
|
void NanoManager::revivePlayer(Player* plr) {
|
||||||
|
CNSocket* sock = PlayerManager::getSockFromID(plr->iID);
|
||||||
|
|
||||||
|
INITSTRUCT(sP_FE2CL_REP_PC_REGEN_SUCC, response);
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_REGEN, resp2);
|
||||||
|
|
||||||
|
plr->HP = PC_MAXHEALTH(plr->level);
|
||||||
|
|
||||||
|
// Nanos
|
||||||
|
int activeSlot = -1;
|
||||||
|
for (int n = 0; n < 3; n++) {
|
||||||
|
int nanoID = plr->equippedNanos[n];
|
||||||
|
if (plr->activeNano == nanoID) {
|
||||||
|
activeSlot = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response parameters
|
||||||
|
response.PCRegenData.iActiveNanoSlotNum = activeSlot;
|
||||||
|
response.PCRegenData.iX = plr->x;
|
||||||
|
response.PCRegenData.iY = plr->y;
|
||||||
|
response.PCRegenData.iZ = plr->z;
|
||||||
|
response.PCRegenData.iHP = plr->HP;
|
||||||
|
response.iFusionMatter = plr->fusionmatter;
|
||||||
|
response.bMoveLocation = 0;
|
||||||
|
response.PCRegenData.iMapNum = 0;
|
||||||
|
|
||||||
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_REGEN_SUCC, sizeof(sP_FE2CL_REP_PC_REGEN_SUCC));
|
||||||
|
|
||||||
|
// Update other players
|
||||||
|
resp2.PCRegenDataForOtherPC.iPC_ID = plr->iID;
|
||||||
|
resp2.PCRegenDataForOtherPC.iX = plr->x;
|
||||||
|
resp2.PCRegenDataForOtherPC.iY = plr->y;
|
||||||
|
resp2.PCRegenDataForOtherPC.iZ = plr->z;
|
||||||
|
resp2.PCRegenDataForOtherPC.iHP = plr->HP;
|
||||||
|
resp2.PCRegenDataForOtherPC.iAngle = plr->angle;
|
||||||
|
resp2.PCRegenDataForOtherPC.Nano = plr->Nanos[plr->activeNano];
|
||||||
|
|
||||||
|
PlayerManager::sendToViewable(sock, (void*)&resp2, P_FE2CL_PC_REGEN, sizeof(sP_FE2CL_PC_REGEN));
|
||||||
|
}
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
@ -30,8 +30,9 @@ struct PassivePower {
|
|||||||
int32_t iCBFlag;
|
int32_t iCBFlag;
|
||||||
int16_t eCharStatusTimeBuffID;
|
int16_t eCharStatusTimeBuffID;
|
||||||
int16_t iValue;
|
int16_t iValue;
|
||||||
|
bool groupPower;
|
||||||
|
|
||||||
PassivePower(std::set<int> p, int16_t t, int32_t f, int16_t b, int16_t a) : powers(p), eSkillType(t), iCBFlag(f), eCharStatusTimeBuffID(b), iValue(a) {}
|
PassivePower(std::set<int> p, int16_t t, int32_t f, int16_t b, int16_t a, bool g) : powers(p), eSkillType(t), iCBFlag(f), eCharStatusTimeBuffID(b), iValue(a), groupPower(g) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NanoData {
|
struct NanoData {
|
||||||
@ -60,8 +61,9 @@ namespace NanoManager {
|
|||||||
void setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId);
|
void setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId);
|
||||||
void resetNanoSkill(CNSocket* sock, int16_t nanoId);
|
void resetNanoSkill(CNSocket* sock, int16_t nanoId);
|
||||||
|
|
||||||
void nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t eSkillType, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue = 0);
|
void nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t eSkillType, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue = 0, bool groupPower = false);
|
||||||
void nanoUnbuff(CNSocket* sock, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue = 0);
|
void nanoUnbuff(CNSocket* sock, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue = 0, bool groupPower = false);
|
||||||
|
|
||||||
int nanoStyle(int nanoId);
|
int nanoStyle(int nanoId);
|
||||||
|
void revivePlayer(Player* plr);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,6 @@ struct Player {
|
|||||||
bool isTradeConfirm;
|
bool isTradeConfirm;
|
||||||
|
|
||||||
bool inCombat;
|
bool inCombat;
|
||||||
bool dotDamage;
|
|
||||||
bool passiveNanoOut;
|
bool passiveNanoOut;
|
||||||
|
|
||||||
int pointDamage;
|
int pointDamage;
|
||||||
@ -61,4 +60,8 @@ struct Player {
|
|||||||
int32_t CurrentMissionID;
|
int32_t CurrentMissionID;
|
||||||
|
|
||||||
sTimeLimitItemDeleteInfo2CL toRemoveVehicle;
|
sTimeLimitItemDeleteInfo2CL toRemoveVehicle;
|
||||||
|
|
||||||
|
int32_t iIDGroup;
|
||||||
|
int groupCnt;
|
||||||
|
int32_t groupIDs[4];
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "CNShared.hpp"
|
#include "CNShared.hpp"
|
||||||
#include "MissionManager.hpp"
|
#include "MissionManager.hpp"
|
||||||
#include "ItemManager.hpp"
|
#include "ItemManager.hpp"
|
||||||
|
#include "NanoManager.hpp"
|
||||||
|
#include "GroupManager.hpp"
|
||||||
#include "ChatManager.hpp"
|
#include "ChatManager.hpp"
|
||||||
|
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
@ -62,6 +64,8 @@ void PlayerManager::addPlayer(CNSocket* key, Player plr) {
|
|||||||
void PlayerManager::removePlayer(CNSocket* key) {
|
void PlayerManager::removePlayer(CNSocket* key) {
|
||||||
PlayerView& view = players[key];
|
PlayerView& view = players[key];
|
||||||
|
|
||||||
|
GroupManager::groupKickPlayer(view.plr);
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_PC_EXIT, exitPacket);
|
INITSTRUCT(sP_FE2CL_PC_EXIT, exitPacket);
|
||||||
exitPacket.iID = players[key].plr->iID;
|
exitPacket.iID = players[key].plr->iID;
|
||||||
|
|
||||||
@ -243,6 +247,9 @@ void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
// TODO: check if serialkey exists, if it doesn't send sP_FE2CL_REP_PC_ENTER_FAIL
|
// TODO: check if serialkey exists, if it doesn't send sP_FE2CL_REP_PC_ENTER_FAIL
|
||||||
Player plr = CNSharedData::getPlayer(enter->iEnterSerialKey);
|
Player plr = CNSharedData::getPlayer(enter->iEnterSerialKey);
|
||||||
|
|
||||||
|
plr.groupCnt = 1;
|
||||||
|
plr.iIDGroup = plr.groupIDs[0] = plr.iID;
|
||||||
|
|
||||||
DEBUGLOG(
|
DEBUGLOG(
|
||||||
std::cout << "P_CL2FE_REQ_PC_ENTER:" << std::endl;
|
std::cout << "P_CL2FE_REQ_PC_ENTER:" << std::endl;
|
||||||
std::cout << "\tID: " << U16toU8(enter->szID) << std::endl;
|
std::cout << "\tID: " << U16toU8(enter->szID) << std::endl;
|
||||||
@ -650,7 +657,6 @@ void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
sP_CL2FE_REQ_PC_GOTO* gotoData = (sP_CL2FE_REQ_PC_GOTO*)data->buf;
|
sP_CL2FE_REQ_PC_GOTO* gotoData = (sP_CL2FE_REQ_PC_GOTO*)data->buf;
|
||||||
INITSTRUCT(sP_FE2CL_REP_PC_GOTO_SUCC, response);
|
INITSTRUCT(sP_FE2CL_REP_PC_GOTO_SUCC, response);
|
||||||
PlayerView& plrv = players[sock];
|
|
||||||
|
|
||||||
DEBUGLOG(
|
DEBUGLOG(
|
||||||
std::cout << "P_CL2FE_REQ_PC_GOTO:" << std::endl;
|
std::cout << "P_CL2FE_REQ_PC_GOTO:" << std::endl;
|
||||||
@ -741,23 +747,35 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
INITSTRUCT(sP_FE2CL_REP_PC_REGEN_SUCC, response);
|
INITSTRUCT(sP_FE2CL_REP_PC_REGEN_SUCC, response);
|
||||||
INITSTRUCT(sP_FE2CL_PC_REGEN, resp2);
|
INITSTRUCT(sP_FE2CL_PC_REGEN, resp2);
|
||||||
|
|
||||||
// Nanos
|
|
||||||
int activeSlot = -1;
|
int activeSlot = -1;
|
||||||
for (int n = 0; n < 3; n++) {
|
|
||||||
int nanoID = plr->equippedNanos[n];
|
|
||||||
plr->Nanos[nanoID].iStamina = 75; // max is 150, so 75 is half
|
|
||||||
response.PCRegenData.Nanos[n] = plr->Nanos[nanoID];
|
|
||||||
if (plr->activeNano == nanoID) {
|
|
||||||
activeSlot = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update player
|
if (reviveData->iRegenType == 3 && plr->iConditionBitFlag & CSB_BIT_PHOENIX) {
|
||||||
|
// nano revive
|
||||||
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
|
NanoManager::nanoUnbuff(sock, CSB_BIT_PHOENIX, ECSB_PHOENIX, 0, false);
|
||||||
|
plr->HP = PC_MAXHEALTH(plr->level);
|
||||||
|
} else {
|
||||||
plr->x = target.x;
|
plr->x = target.x;
|
||||||
plr->y = target.y;
|
plr->y = target.y;
|
||||||
plr->z = target.z;
|
plr->z = target.z;
|
||||||
|
|
||||||
|
if (reviveData->iRegenType != 5)
|
||||||
plr->HP = PC_MAXHEALTH(plr->level);
|
plr->HP = PC_MAXHEALTH(plr->level);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int nanoID = plr->equippedNanos[i];
|
||||||
|
|
||||||
|
// halve nano health if respawning
|
||||||
|
if (reviveData->iRegenType != 5) {
|
||||||
|
plr->Nanos[nanoID].iStamina = 75; // max is 150, so 75 is half
|
||||||
|
response.PCRegenData.Nanos[i] = plr->Nanos[nanoID];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plr->activeNano == nanoID)
|
||||||
|
activeSlot = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Response parameters
|
// Response parameters
|
||||||
response.PCRegenData.iActiveNanoSlotNum = activeSlot;
|
response.PCRegenData.iActiveNanoSlotNum = activeSlot;
|
||||||
response.PCRegenData.iX = plr->x;
|
response.PCRegenData.iX = plr->x;
|
||||||
@ -765,8 +783,8 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
response.PCRegenData.iZ = plr->z;
|
response.PCRegenData.iZ = plr->z;
|
||||||
response.PCRegenData.iHP = plr->HP;
|
response.PCRegenData.iHP = plr->HP;
|
||||||
response.iFusionMatter = plr->fusionmatter;
|
response.iFusionMatter = plr->fusionmatter;
|
||||||
response.bMoveLocation = reviveData->eIL;
|
response.bMoveLocation = 0;
|
||||||
response.PCRegenData.iMapNum = reviveData->iIndex;
|
response.PCRegenData.iMapNum = 0;
|
||||||
|
|
||||||
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_REGEN_SUCC, sizeof(sP_FE2CL_REP_PC_REGEN_SUCC));
|
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_REGEN_SUCC, sizeof(sP_FE2CL_REP_PC_REGEN_SUCC));
|
||||||
|
|
||||||
@ -777,6 +795,9 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
resp2.PCRegenDataForOtherPC.iZ = plr->z;
|
resp2.PCRegenDataForOtherPC.iZ = plr->z;
|
||||||
resp2.PCRegenDataForOtherPC.iHP = plr->HP;
|
resp2.PCRegenDataForOtherPC.iHP = plr->HP;
|
||||||
resp2.PCRegenDataForOtherPC.iAngle = plr->angle;
|
resp2.PCRegenDataForOtherPC.iAngle = plr->angle;
|
||||||
|
resp2.PCRegenDataForOtherPC.iConditionBitFlag = plr->iConditionBitFlag;
|
||||||
|
resp2.PCRegenDataForOtherPC.iPCState = plr->iPCState;
|
||||||
|
resp2.PCRegenDataForOtherPC.iSpecialState = plr->iSpecialState;
|
||||||
resp2.PCRegenDataForOtherPC.Nano = plr->Nanos[plr->activeNano];
|
resp2.PCRegenDataForOtherPC.Nano = plr->Nanos[plr->activeNano];
|
||||||
|
|
||||||
sendToViewable(sock, (void*)&resp2, P_FE2CL_PC_REGEN, sizeof(sP_FE2CL_PC_REGEN));
|
sendToViewable(sock, (void*)&resp2, P_FE2CL_PC_REGEN, sizeof(sP_FE2CL_PC_REGEN));
|
||||||
@ -929,8 +950,15 @@ void PlayerManager::setSpecialState(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
Player *plr = getPlayer(sock);
|
Player *plr = getPlayer(sock);
|
||||||
|
|
||||||
|
if (plr == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH* setData = (sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH*)data->buf;
|
sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH* setData = (sP_CL2FE_GM_REQ_PC_SPECIAL_STATE_SWITCH*)data->buf;
|
||||||
|
|
||||||
|
// HACK: work around the invisible weapon bug
|
||||||
|
if (setData->iSpecialStateFlag == CN_SPECIAL_STATE_FLAG__FULL_UI)
|
||||||
|
ItemManager::updateEquips(sock, plr);
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_PC_SPECIAL_STATE_CHANGE, response);
|
INITSTRUCT(sP_FE2CL_PC_SPECIAL_STATE_CHANGE, response);
|
||||||
|
|
||||||
plr->iSpecialState ^= setData->iSpecialStateFlag;
|
plr->iSpecialState ^= setData->iSpecialStateFlag;
|
||||||
@ -943,6 +971,14 @@ void PlayerManager::setSpecialState(CNSocket* sock, CNPacketData* data) {
|
|||||||
sendToViewable(sock, (void*)&response, P_FE2CL_PC_SPECIAL_STATE_CHANGE, sizeof(sP_FE2CL_PC_SPECIAL_STATE_CHANGE));
|
sendToViewable(sock, (void*)&response, P_FE2CL_PC_SPECIAL_STATE_CHANGE, sizeof(sP_FE2CL_PC_SPECIAL_STATE_CHANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player *PlayerManager::getPlayerFromID(int32_t iID) {
|
||||||
|
for (auto& pair : PlayerManager::players)
|
||||||
|
if (pair.second.plr->iID == iID)
|
||||||
|
return pair.second.plr;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
CNSocket *PlayerManager::getSockFromID(int32_t iID) {
|
CNSocket *PlayerManager::getSockFromID(int32_t iID) {
|
||||||
for (auto& pair : PlayerManager::players)
|
for (auto& pair : PlayerManager::players)
|
||||||
if (pair.second.plr->iID == iID)
|
if (pair.second.plr->iID == iID)
|
||||||
|
@ -69,5 +69,6 @@ namespace PlayerManager {
|
|||||||
bool isAccountInUse(int accountId);
|
bool isAccountInUse(int accountId);
|
||||||
void exitDuplicate(int accountId);
|
void exitDuplicate(int accountId);
|
||||||
void setSpecialState(CNSocket* sock, CNPacketData* data);
|
void setSpecialState(CNSocket* sock, CNPacketData* data);
|
||||||
|
Player *getPlayerFromID(int32_t iID);
|
||||||
CNSocket *getSockFromID(int32_t iID);
|
CNSocket *getSockFromID(int32_t iID);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "CNShardServer.hpp"
|
#include "CNShardServer.hpp"
|
||||||
#include "CNStructs.hpp"
|
#include "CNStructs.hpp"
|
||||||
#include "PlayerManager.hpp"
|
#include "PlayerManager.hpp"
|
||||||
|
#include "NanoManager.hpp"
|
||||||
#include "TransportManager.hpp"
|
#include "TransportManager.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -153,6 +154,7 @@ void TransportManager::transportWarpHandler(CNSocket* sock, CNPacketData* data)
|
|||||||
break;
|
break;
|
||||||
case 2: // Monkey Skyway
|
case 2: // Monkey Skyway
|
||||||
if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) { // check if route exists
|
if (SkywayPaths.find(route.mssRouteNum) != SkywayPaths.end()) { // check if route exists
|
||||||
|
NanoManager::summonNano(sock, -1); // make sure that no nano is active during the ride
|
||||||
SkywayQueues[sock] = SkywayPaths[route.mssRouteNum]; // set socket point queue to route
|
SkywayQueues[sock] = SkywayPaths[route.mssRouteNum]; // set socket point queue to route
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "Database.hpp"
|
#include "Database.hpp"
|
||||||
#include "TableData.hpp"
|
#include "TableData.hpp"
|
||||||
#include "ChunkManager.hpp"
|
#include "ChunkManager.hpp"
|
||||||
|
#include "GroupManager.hpp"
|
||||||
|
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
|
||||||
@ -99,6 +100,7 @@ int main() {
|
|||||||
NPCManager::init();
|
NPCManager::init();
|
||||||
TransportManager::init();
|
TransportManager::init();
|
||||||
// BuddyManager::init(); // stubbed until we have database integration + lots of bug fixes
|
// BuddyManager::init(); // stubbed until we have database integration + lots of bug fixes
|
||||||
|
GroupManager::init();
|
||||||
|
|
||||||
Database::open();
|
Database::open();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user