diff --git a/src/Abilities.cpp b/src/Abilities.cpp index 14d4f0a..8b4d19c 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -97,8 +97,13 @@ static SkillResult handleSkillBatteryDrain(SkillData* skill, int power, ICombata static SkillResult handleSkillMove(SkillData* skill, int power, ICombatant* source, ICombatant* target) { if(source->getCharType() != 1) return SkillResult(); // only Players are valid sources for recall + Player* plr = dynamic_cast(source); - PlayerManager::sendPlayerTo(source->getRef().sock, plr->recallX, plr->recallY, plr->recallZ, plr->recallInstance); + if(source == target) { + // no trailing struct for self + PlayerManager::sendPlayerTo(target->getRef().sock, plr->recallX, plr->recallY, plr->recallZ, plr->recallInstance); + return SkillResult(); + } sSkillResult_Move result{}; result.eCT = target->getCharType(); @@ -230,7 +235,12 @@ void Abilities::useNanoSkill(CNSocket* sock, SkillData* skill, sNano& nano, std: attachSkillResults(results, resultSize, (uint8_t*)(pkt + 1)); sock->sendPacket(pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); - PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); + + if(skill->skillType == SkillType::RECALL_GROUP) + // group recall packet is sent only to group members + PlayerManager::sendToGroup(sock, pkt, P_FE2CL_NANO_SKILL_USE, resplen); + else + PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE, resplen); if (nano.iStamina <= 0) Nanos::summonNano(sock, -1); @@ -283,9 +293,6 @@ static std::vector entityRefsToCombatants(std::vector re std::vector Abilities::matchTargets(ICombatant* src, SkillData* skill, int count, int32_t *ids) { - if(skill->effectTarget == SkillEffectTarget::SELF) - return {src}; // client sends 0 targets for certain self-targeting skills (recall) - if(skill->targetType == SkillTargetType::GROUP) { // group if(count != 1 || ids[0] != src->getID()) { @@ -295,6 +302,10 @@ std::vector Abilities::matchTargets(ICombatant* src, SkillData* ski return entityRefsToCombatants(src->getGroupMembers()); } + // this check *has* to happen after the group check above due to cases like group recall that use both + if(skill->effectTarget == SkillEffectTarget::SELF) + return {src}; // client sends 0 targets for certain self-targeting skills (recall) + // individuals std::vector targets; for (int i = 0; i < count; i++) { diff --git a/src/PlayerManager.cpp b/src/PlayerManager.cpp index dfd0e5a..c07df89 100644 --- a/src/PlayerManager.cpp +++ b/src/PlayerManager.cpp @@ -338,6 +338,14 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) { delete lm; } +void PlayerManager::sendToGroup(CNSocket* sock, void* buf, uint32_t type, size_t size) { + Player* plr = getPlayer(sock); + if (plr->group == nullptr) + return; + for(const EntityRef& ref : plr->group->filter(EntityKind::PLAYER)) + ref.sock->sendPacket(buf, type, size); +} + void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) { Player* plr = getPlayer(sock); for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) { diff --git a/src/PlayerManager.hpp b/src/PlayerManager.hpp index 1e15ddc..ad9c767 100644 --- a/src/PlayerManager.hpp +++ b/src/PlayerManager.hpp @@ -33,6 +33,7 @@ namespace PlayerManager { CNSocket *getSockFromAny(int by, int id, int uid, std::string firstname, std::string lastname); WarpLocation *getRespawnPoint(Player *plr); + void sendToGroup(CNSocket *sock, void* buf, uint32_t type, size_t size); void sendToViewable(CNSocket *sock, void* buf, uint32_t type, size_t size); // TODO: unify this under the new Entity system