diff --git a/src/Chat.cpp b/src/Chat.cpp index 61b9803..f692a3e 100644 --- a/src/Chat.cpp +++ b/src/Chat.cpp @@ -225,10 +225,6 @@ static void tradeChatHandler(CNSocket* sock, CNPacketData* data) { static void groupChatHandler(CNSocket* sock, CNPacketData* data) { 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 (otherPlr == nullptr) - return; std::string fullChat = sanitizeText(AUTOU16TOU8(chat->szFreeChat)); @@ -251,16 +247,15 @@ static void groupChatHandler(CNSocket* sock, CNPacketData* data) { resp.iSendPCID = plr->iID; resp.iEmoteCode = chat->iEmoteCode; - Groups::sendToGroup(otherPlr, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC)); + if (plr->group == nullptr) + sock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC)); + else + Groups::sendToGroup(plr->group, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC)); } static void groupMenuChatHandler(CNSocket* sock, CNPacketData* data) { 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 (otherPlr == nullptr) - return; std::string fullChat = sanitizeText(AUTOU16TOU8(chat->szFreeChat)); std::string logLine = "[GroupMenuChat] " + PlayerManager::getPlayerName(plr, true) + ": " + fullChat; @@ -275,7 +270,9 @@ static void groupMenuChatHandler(CNSocket* sock, CNPacketData* data) { resp.iSendPCID = plr->iID; resp.iEmoteCode = chat->iEmoteCode; - Groups::sendToGroup(otherPlr, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC)); + if (plr->group == nullptr) + sock->sendPacket((void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC)); + Groups::sendToGroup(plr->group, (void*)&resp, P_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC)); } // we only allow plain ascii, at least for now diff --git a/src/Combat.cpp b/src/Combat.cpp index 0082363..5f08f9e 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -268,17 +268,10 @@ void Combat::npcAttackPc(Mob *mob, time_t currTime) { * single RNG roll per mission task, and every group member shares that same * set of rolls. */ -void Combat::genQItemRolls(Player *leader, std::map& rolls) { - for (int i = 0; i < leader->groupCnt; i++) { - if (leader->groupIDs[i] == 0) - continue; - - CNSocket *otherSock = PlayerManager::getSockFromID(leader->groupIDs[i]); - if (otherSock == nullptr) - continue; - - Player *member = PlayerManager::getPlayer(otherSock); +void Combat::genQItemRolls(std::vector players, std::map& rolls) { + for (int i = 0; i < players.size(); i++) { + Player* member = players[i]; for (int j = 0; j < ACTIVE_MISSION_COUNT; j++) if (member->tasks[j] != 0) rolls[member->tasks[j]] = Rand::rand(); @@ -679,7 +672,7 @@ static void playerTick(CNServer *serv, time_t currTime) { bool transmit = false; // group ticks - if (plr->groupCnt > 1) + if (plr->group != nullptr) Groups::groupTickInfo(plr); // do not tick dead players diff --git a/src/Combat.hpp b/src/Combat.hpp index 14d0076..5b53c86 100644 --- a/src/Combat.hpp +++ b/src/Combat.hpp @@ -25,5 +25,5 @@ namespace Combat { void init(); void npcAttackPc(Mob *mob, time_t currTime); - void genQItemRolls(Player* leader, std::map& rolls); + void genQItemRolls(std::vector players, std::map& rolls); } diff --git a/src/Eggs.cpp b/src/Eggs.cpp index a3b0fa2..64b1e93 100644 --- a/src/Eggs.cpp +++ b/src/Eggs.cpp @@ -16,10 +16,9 @@ std::unordered_map Eggs::EggTypes; int Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) { Player* plr = PlayerManager::getPlayer(sock); - Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); - int bitFlag = Groups::getGroupFlags(otherPlr); // TODO ABILITIES + int bitFlag = plr->group->conditionBitFlag; int CBFlag = 0;// Abilities::applyBuff(sock, skillId, 1, 3, bitFlag); size_t resplen; @@ -98,9 +97,8 @@ static void eggStep(CNServer* serv, time_t currTime) { CNSocket* sock = it->first.first; int32_t CBFlag = it->first.second; Player* plr = PlayerManager::getPlayer(sock); - Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); - int groupFlags = Groups::getGroupFlags(otherPlr); + int groupFlags = plr->group->conditionBitFlag; // TODO ABILITIES //for (auto& pwr : Abilities::Powers) { // if (pwr.bitFlag == CBFlag) { // pick the power with the right flag and unbuff diff --git a/src/Entities.hpp b/src/Entities.hpp index 41d9dfd..f3f061a 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -25,6 +25,7 @@ enum class AIState { }; class Chunk; +struct Group; struct Entity { EntityKind kind = EntityKind::INVALID; @@ -130,6 +131,7 @@ struct CombatNPC : public BaseNPC, public ICombatant { int level = 0; int speed = 300; AIState state = AIState::INACTIVE; + Group* group = nullptr; int playersInView = 0; // for optimizing away AI in empty chunks std::map stateHandlers; diff --git a/src/Groups.cpp b/src/Groups.cpp index 28d76e4..ba85d4c 100644 --- a/src/Groups.cpp +++ b/src/Groups.cpp @@ -19,22 +19,65 @@ using namespace Groups; +void Groups::addToGroup(EntityRef member, Group* group) { + if (member.kind == EntityKind::PLAYER) { + Player* plr = PlayerManager::getPlayer(member.sock); + plr->group = group; + } + else if (member.kind == EntityKind::COMBAT_NPC) { + CombatNPC* npc = (CombatNPC*)member.getEntity(); + npc->group = group; + } + else { + std::cout << "[WARN] Adding a weird entity type to a group" << std::endl; + } + + group->members.push_back(member); +} + +void Groups::removeFromGroup(EntityRef member, Group* group) { + if (member.kind == EntityKind::PLAYER) { + Player* plr = PlayerManager::getPlayer(member.sock); + plr->group = nullptr; // no dangling pointers here muahaahahah + } + else if (member.kind == EntityKind::COMBAT_NPC) { + CombatNPC* npc = (CombatNPC*)member.getEntity(); + npc->group = nullptr; + } + else { + std::cout << "[WARN] Adding a weird entity type to a group" << std::endl; + } + + auto it = std::find(group->members.begin(), group->members.end(), member); + if (it == group->members.end()) { + std::cout << "[WARN] Tried to remove a member that isn't in the group" << std::endl; + return; + } + + group->members.erase(it); + + if (group->members.empty()) delete group; // cleanup memory +} + +void Groups::disbandGroup(Group* group) { + // remove everyone from the group!! + std::vector members = group->members; + for (EntityRef member : members) { + removeFromGroup(member, group); + } +} + static void requestGroup(CNSocket* sock, CNPacketData* data) { 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 (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->iIDGroup != otherPlr->iID || otherPlr->groupCnt > 1) { + if ((plr->group != nullptr && (*plr->group)[EntityKind::PLAYER].size() >= 4) || otherPlr->group != nullptr) { 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; @@ -77,28 +120,29 @@ static void joinGroup(CNSocket* sock, CNPacketData* data) { if (otherPlr == nullptr) return; - otherPlr = PlayerManager::getPlayerFromID(otherPlr->iIDGroup); - - if (otherPlr == nullptr) - return; + int size = otherPlr->group == nullptr ? 1 : (*otherPlr->group)[EntityKind::PLAYER].size(); // fail if the group is full or the other player is already in a group - if (plr->groupCnt > 1 || plr->iIDGroup != plr->iID || otherPlr->groupCnt >= 4) { + if (plr->group != nullptr || size + 1 > 4) { INITSTRUCT(sP_FE2CL_PC_GROUP_JOIN_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))) { + if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_JOIN), size + 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; + if (otherPlr->group == nullptr) { + // create group + otherPlr->group = new Group(); // spooky + addToGroup(PlayerManager::getSockFromID(recv->iID_From), otherPlr->group); + } + addToGroup(sock, otherPlr->group); + auto players = (*otherPlr->group)[EntityKind::PLAYER]; - size_t resplen = sizeof(sP_FE2CL_PC_GROUP_JOIN) + otherPlr->groupCnt * sizeof(sPCGroupMemberInfo); + size_t resplen = sizeof(sP_FE2CL_PC_GROUP_JOIN) + players.size() * sizeof(sPCGroupMemberInfo); uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; memset(respbuf, 0, resplen); @@ -107,13 +151,13 @@ static void joinGroup(CNSocket* sock, CNPacketData* data) { sPCGroupMemberInfo *respdata = (sPCGroupMemberInfo*)(respbuf+sizeof(sP_FE2CL_PC_GROUP_JOIN)); resp->iID_NewMember = plr->iID; - resp->iMemberPCCnt = otherPlr->groupCnt; + resp->iMemberPCCnt = players.size(); - int bitFlag = getGroupFlags(otherPlr); + int bitFlag = otherPlr->group->conditionBitFlag; + for (int i = 0; i < players.size(); i++) { - for (int i = 0; i < otherPlr->groupCnt; i++) { - Player* varPlr = PlayerManager::getPlayerFromID(otherPlr->groupIDs[i]); - CNSocket* sockTo = PlayerManager::getSockFromID(otherPlr->groupIDs[i]); + Player* varPlr = PlayerManager::getPlayer(players[i].sock); + CNSocket* sockTo = players[i].sock; if (varPlr == nullptr || sockTo == nullptr) continue; @@ -143,37 +187,32 @@ static void joinGroup(CNSocket* sock, CNPacketData* data) { } } - sendToGroup(otherPlr, (void*)&respbuf, P_FE2CL_PC_GROUP_JOIN, resplen); + Groups::sendToGroup(otherPlr->group, (void*)&respbuf, P_FE2CL_PC_GROUP_JOIN, resplen); } static void leaveGroup(CNSocket* sock, CNPacketData* data) { Player* plr = PlayerManager::getPlayer(sock); - groupKickPlayer(plr); + groupKick(plr); } -void Groups::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; - } - +void Groups::sendToGroup(Group* group, void* buf, uint32_t type, size_t size) { + auto players = (*group)[EntityKind::PLAYER]; + for (int i = 0; i < players.size(); i++) { + CNSocket* sock = players[i].sock; sock->sendPacket(buf, type, size); } } void Groups::groupTickInfo(Player* plr) { - if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO), plr->groupCnt, sizeof(sPCGroupMemberInfo))) { + + auto players = (*plr->group)[EntityKind::PLAYER]; + + if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO), players.size(), 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); + size_t resplen = sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO) + players.size() * sizeof(sPCGroupMemberInfo); uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; memset(respbuf, 0, resplen); @@ -182,10 +221,11 @@ void Groups::groupTickInfo(Player* plr) { sPCGroupMemberInfo *respdata = (sPCGroupMemberInfo*)(respbuf+sizeof(sP_FE2CL_PC_GROUP_MEMBER_INFO)); resp->iID = plr->iID; - resp->iMemberPCCnt = plr->groupCnt; + resp->iMemberPCCnt = players.size(); - for (int i = 0; i < plr->groupCnt; i++) { - Player* varPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]); + for (int i = 0; i < players.size(); i++) { + EntityRef member = players[i]; + Player* varPlr = PlayerManager::getPlayer(member.sock); if (varPlr == nullptr) continue; @@ -210,17 +250,17 @@ void Groups::groupTickInfo(Player* plr) { } } - sendToGroup(plr, (void*)&respbuf, P_FE2CL_PC_GROUP_MEMBER_INFO, resplen); + sendToGroup(plr->group, (void*)&respbuf, P_FE2CL_PC_GROUP_MEMBER_INFO, resplen); } static void groupUnbuff(Player* plr) { - for (int i = 0; i < plr->groupCnt; i++) { - for (int n = 0; n < plr->groupCnt; n++) { + Group* group = plr->group; + for (int i = 0; i < group->members.size(); i++) { + for (int n = 0; n < group->members.size(); n++) { if (i == n) continue; - Player* otherPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]); - CNSocket* sock = PlayerManager::getSockFromID(plr->groupIDs[n]); + EntityRef other = group->members[n]; // TODO ABILITIES //Abilities::applyBuff(sock, otherPlr->Nanos[otherPlr->activeNano].iSkillID, 2, 1, 0); @@ -228,27 +268,26 @@ static void groupUnbuff(Player* plr) { } } -void Groups::groupKickPlayer(Player* plr) { +void Groups::groupKick(Player* plr) { + Group* group = plr->group; + // if you are the group leader, destroy your own group and kick everybody - if (plr->iID == plr->iIDGroup) { + if (plr->group->members[0] == PlayerManager::getSockFromID(plr->iID)) { groupUnbuff(plr); 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; + sendToGroup(plr->group, (void*)&resp1, P_FE2CL_PC_GROUP_LEAVE_SUCC, sizeof(sP_FE2CL_PC_GROUP_LEAVE_SUCC)); + disbandGroup(plr->group); return; } - Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); + auto players = (*group)[EntityKind::PLAYER]; - if (otherPlr == nullptr) - return; - - if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_LEAVE), otherPlr->groupCnt - 1, sizeof(sPCGroupMemberInfo))) { + if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_LEAVE), players.size() - 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); + size_t resplen = sizeof(sP_FE2CL_PC_GROUP_LEAVE) + (players.size() - 1) * sizeof(sPCGroupMemberInfo); uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; memset(respbuf, 0, resplen); @@ -257,78 +296,55 @@ void Groups::groupKickPlayer(Player* plr) { sPCGroupMemberInfo *respdata = (sPCGroupMemberInfo*)(respbuf+sizeof(sP_FE2CL_PC_GROUP_LEAVE)); resp->iID_LeaveMember = plr->iID; - resp->iMemberPCCnt = otherPlr->groupCnt - 1; + resp->iMemberPCCnt = players.size() - 1; - int bitFlag = getGroupFlags(otherPlr) & ~plr->iGroupConditionBitFlag; - int moveDown = 0; + int bitFlag = 0; // TODO ABILITIES getGroupFlags(otherPlr) & ~plr->iGroupConditionBitFlag; CNSocket* sock = PlayerManager::getSockFromID(plr->iID); if (sock == nullptr) return; - for (int i = 0; i < otherPlr->groupCnt; i++) { - Player* varPlr = PlayerManager::getPlayerFromID(otherPlr->groupIDs[i]); - CNSocket* sockTo = PlayerManager::getSockFromID(otherPlr->groupIDs[i]); + removeFromGroup(sock, group); + + players = (*group)[EntityKind::PLAYER]; + for (int i = 0; i < players.size(); i++) { + CNSocket* sockTo = players[i].sock; + Player* varPlr = PlayerManager::getPlayer(sock); if (varPlr == nullptr || sockTo == 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; + 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 - if (varPlr == plr) { - moveDown = 1; - otherPlr->groupIDs[i] = 0; - } else { // remove the leaving member's buffs from the group and remove the group buffs from the leaving member. + // remove the leaving member's buffs from the group and remove the group buffs from the leaving member. // TODO ABILITIES /*if (Abilities::SkillTable[varPlr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) Abilities::applyBuff(sock, varPlr->Nanos[varPlr->activeNano].iSkillID, 2, 1, 0); if (Abilities::SkillTable[plr->Nanos[varPlr->activeNano].iSkillID].targetType == 3) Abilities::applyBuff(sockTo, plr->Nanos[plr->activeNano].iSkillID, 2, 1, bitFlag);*/ - } } - plr->iIDGroup = plr->iID; - otherPlr->groupCnt -= 1; - - sendToGroup(otherPlr, (void*)&respbuf, P_FE2CL_PC_GROUP_LEAVE, resplen); + sendToGroup(group, (void*)&respbuf, P_FE2CL_PC_GROUP_LEAVE, resplen); 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)); } -int Groups::getGroupFlags(Player* plr) { - int bitFlag = 0; - - for (int i = 0; i < plr->groupCnt; i++) { - Player* otherPlr = PlayerManager::getPlayerFromID(plr->groupIDs[i]); - - if (otherPlr == nullptr) - continue; - - bitFlag |= otherPlr->iGroupConditionBitFlag; - } - - return bitFlag; -} - void Groups::init() { REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE, requestGroup); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GROUP_INVITE_REFUSE, refuseGroup); diff --git a/src/Groups.hpp b/src/Groups.hpp index b12f9f9..b437827 100644 --- a/src/Groups.hpp +++ b/src/Groups.hpp @@ -1,17 +1,36 @@ #pragma once -#include "Player.hpp" #include "core/Core.hpp" #include "servers/CNShardServer.hpp" +#include "Entities.hpp" #include #include +struct Player; +enum EntityKind; + +struct Group { + std::vector members; + int32_t conditionBitFlag; + + auto operator[](EntityKind kind) { + std::vector filtered; + std::copy_if(members.begin(), members.end(), std::back_inserter(filtered), [kind](EntityRef e) { + return e.kind == kind; + }); + return filtered; + } +}; + namespace Groups { void init(); - void sendToGroup(Player* plr, void* buf, uint32_t type, size_t size); + void sendToGroup(Group* group, void* buf, uint32_t type, size_t size); void groupTickInfo(Player* plr); - void groupKickPlayer(Player* plr); - int getGroupFlags(Player* plr); + void groupKick(Player* plr); + + void addToGroup(EntityRef member, Group* group); + void removeFromGroup(EntityRef member, Group* group); + void disbandGroup(Group* group); } diff --git a/src/MobAI.cpp b/src/MobAI.cpp index 9a930ec..1c9b860 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -842,22 +842,20 @@ void MobAI::onDeath(CombatNPC* npc, EntityRef src) { Items::DropRoll rolled; Items::DropRoll eventRolled; std::map qitemRolls; + std::vector playerRefs; - Player* leader = PlayerManager::getPlayerFromID(plr->iIDGroup); - assert(leader != nullptr); // should never happen - - Combat::genQItemRolls(leader, qitemRolls); - - if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) { + if (plr->group == nullptr) { + playerRefs.push_back(plr); + Combat::genQItemRolls(playerRefs, qitemRolls); Items::giveMobDrop(src.sock, self, rolled, eventRolled); Missions::mobKilled(src.sock, self->type, qitemRolls); } else { - for (int i = 0; i < leader->groupCnt; i++) { - CNSocket* sockTo = PlayerManager::getSockFromID(leader->groupIDs[i]); - if (sockTo == nullptr) - continue; - + auto players = (*plr->group)[EntityKind::PLAYER]; + for (EntityRef pRef : players) playerRefs.push_back(PlayerManager::getPlayer(pRef.sock)); + Combat::genQItemRolls(playerRefs, qitemRolls); + for (int i = 0; i < players.size(); i++) { + CNSocket* sockTo = players[i].sock; Player* otherPlr = PlayerManager::getPlayer(sockTo); // only contribute to group members' kills if they're close enough diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 6b3b253..682f549 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -182,9 +182,12 @@ static void handleWarp(CNSocket* sock, int32_t warpId) { if (Warps[warpId].isInstance) { uint64_t instanceID = Warps[warpId].instanceID; + Player* leader = plr; + if (plr->group != nullptr) leader = PlayerManager::getPlayer((*plr->group)[EntityKind::PLAYER][0].sock); + // if warp requires you to be on a mission, it's gotta be a unique instance if (Warps[warpId].limitTaskID != 0 || instanceID == 14) { // 14 is a special case for the Time Lab - instanceID += ((uint64_t)plr->iIDGroup << 32); // upper 32 bits are leader ID + instanceID += ((uint64_t)leader->iID << 32); // upper 32 bits are leader ID Chunking::createInstance(instanceID); // save Lair entrance coords as a pseudo-Resurrect 'Em @@ -194,14 +197,13 @@ static void handleWarp(CNSocket* sock, int32_t warpId) { plr->recallInstance = instanceID; } - if (plr->iID == plr->iIDGroup && plr->groupCnt == 1) + if (plr->group == nullptr) PlayerManager::sendPlayerTo(sock, Warps[warpId].x, Warps[warpId].y, Warps[warpId].z, instanceID); else { - Player* leaderPlr = PlayerManager::getPlayerFromID(plr->iIDGroup); - - for (int i = 0; i < leaderPlr->groupCnt; i++) { - Player* otherPlr = PlayerManager::getPlayerFromID(leaderPlr->groupIDs[i]); - CNSocket* sockTo = PlayerManager::getSockFromID(leaderPlr->groupIDs[i]); + auto players = (*plr->group)[EntityKind::PLAYER]; + for (int i = 0; i < players.size(); i++) { + CNSocket* sockTo = players[i].sock; + Player* otherPlr = PlayerManager::getPlayer(sockTo); if (otherPlr == nullptr || sockTo == nullptr) continue; diff --git a/src/Player.hpp b/src/Player.hpp index c10446a..84513b8 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -6,6 +6,7 @@ #include "core/Core.hpp" #include "Chunking.hpp" #include "Entities.hpp" +#include "Groups.hpp" #define ACTIVE_MISSION_COUNT 6 @@ -65,10 +66,7 @@ struct Player : public Entity, public ICombatant { sTimeLimitItemDeleteInfo2CL toRemoveVehicle = {}; - int32_t iIDGroup = 0; - int groupCnt = 0; - int32_t groupIDs[4] = {}; - int32_t iGroupConditionBitFlag = 0; + Group* group = nullptr; bool notify = false; bool hidden = false; diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index e79bde9..1dcc579 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -41,7 +41,8 @@ void PlayerManager::removePlayer(CNSocket* key) { Player* plr = getPlayer(key); uint64_t fromInstance = plr->instanceID; - Groups::groupKickPlayer(plr); + if(plr->group != nullptr) + Groups::groupKick(plr); // remove player's bullets Combat::Bullets.erase(plr->iID); @@ -232,8 +233,7 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) { Database::getPlayer(plr, lm->playerId); } - plr->groupCnt = 1; - plr->iIDGroup = plr->groupIDs[0] = plr->iID; + plr->group = nullptr; response.iID = plr->iID; response.uiSvrTime = getTime(); @@ -474,9 +474,8 @@ static void revivePlayer(CNSocket* sock, CNPacketData* data) { resp2.PCRegenDataForOtherPC.iHP = plr->HP; resp2.PCRegenDataForOtherPC.iAngle = plr->angle; - Player *otherPlr = getPlayerFromID(plr->iIDGroup); - if (otherPlr != nullptr) { - int bitFlag = Groups::getGroupFlags(otherPlr); + if (plr->group != nullptr) { + int bitFlag = plr->group->conditionBitFlag; resp2.PCRegenDataForOtherPC.iConditionBitFlag = plr->iConditionBitFlag = plr->iSelfConditionBitFlag | bitFlag; resp2.PCRegenDataForOtherPC.iPCState = plr->iPCState;