diff --git a/src/Abilities.cpp b/src/Abilities.cpp index 4b23152..c9d0d61 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -3,6 +3,7 @@ #include "NPCManager.hpp" #include "PlayerManager.hpp" #include "Buffs.hpp" +#include "Nanos.hpp" #include @@ -10,14 +11,92 @@ using namespace Abilities; std::map Abilities::SkillTable; -/* -// New email notification -static void emailUpdateCheck(CNSocket* sock, CNPacketData* data) { - INITSTRUCT(sP_FE2CL_REP_PC_NEW_EMAIL, resp); - resp.iNewEmailCnt = Database::getUnreadEmailCount(PlayerManager::getPlayer(sock)->iID); - sock->sendPacket(resp, P_FE2CL_REP_PC_NEW_EMAIL); +#pragma region Skill handlers +static SkillResult handleSkillBuff(SkillData* skill, ICombatant* target) { + // the buff skill is special in that its application is not aligned + // with the SKILL_USE_SUCC packet, since buffs are calculated every tick. + // thus, no game state changes should happen here. + sSkillResult_Buff result{}; + result.iConditionBitFlag = target->getCompositeCondition(); + result.iID = target->getID(); + result.bProtected = 0; + result.eCT = target->getCharType(); + return SkillResult(sizeof(sSkillResult_Buff), &result); +} +#pragma endregion + +void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector affected) { + if(SkillTable.count(nano.iSkillID) == 0) + return; + + SkillData* skill = &SkillTable[nano.iSkillID]; + Player* plr = PlayerManager::getPlayer(sock); + int count = affected.size(); + + size_t resultSize = 0; + SkillResult (*skillHandler)(SkillData*, ICombatant*) = nullptr; + switch(skill->skillType) + { + case EST_JUMP: + case EST_RUN: + case EST_FREEDOM: + case EST_PHOENIX: + case EST_INVULNERABLE: + case EST_MINIMAPENEMY: + case EST_MINIMAPTRESURE: + case EST_NANOSTIMPAK: + case EST_PROTECTBATTERY: + case EST_PROTECTINFECTION: + case EST_REWARDBLOB: + case EST_REWARDCASH: + case EST_STAMINA_SELF: + case EST_STEALTH: + resultSize = sizeof(sSkillResult_Buff); + skillHandler = handleSkillBuff; + break; + default: + std::cout << "[WARN] Unhandled skill type " << skill->skillType << std::endl; + return; + } + + std::vector results; + for(ICombatant* target : affected) { + SkillResult result = handleSkillBuff(skill, target); + if(result.size != resultSize) { + std::cout << "[WARN] bad skill result size for " << skill->skillType << " from " << handleSkillBuff << std::endl; + return; + } + results.push_back(result); + } + + if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), count, resultSize)) { + std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE_SUCC packet size\n"; + return; + } + + // initialize response struct + size_t resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + count * resultSize; + uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; + memset(respbuf, 0, resplen); + + sP_FE2CL_NANO_SKILL_USE_SUCC* pkt = (sP_FE2CL_NANO_SKILL_USE_SUCC*)respbuf; + pkt->iPC_ID = plr->iID; + pkt->iNanoID = nano.iID; + pkt->iSkillID = nano.iSkillID; + pkt->iNanoStamina = nano.iStamina; + pkt->bNanoDeactive = nano.iStamina <= 0; + pkt->eST = skill->skillType; + pkt->iTargetCnt = count; + + char* appended = (char*)(pkt + 1); + for(SkillResult& result : results) { + memcpy(appended, result.payload, resultSize); + appended += resultSize; + } + + sock->sendPacket(pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); + PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen); } -*/ std::vector Abilities::matchTargets(SkillData* skill, int count, int32_t *ids) { @@ -103,13 +182,11 @@ int Abilities::getCSTBFromST(int eSkillType) { return result; } -#pragma region Skill Handlers -bool Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) { +std::vector Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr) { assert(skill->drainType == SkillDrainType::PASSIVE); - bool newBuffApplied = false; - EntityRef self = PlayerManager::getSockFromID(plr->iID); + std::vector affected; std::vector targets; if (skill->targetType == SkillTargetType::GROUP) { targets = plr->getGroupMembers(); // group @@ -121,6 +198,7 @@ bool Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) { } int timeBuffId = getCSTBFromST(skill->skillType); + int boost = Nanos::getNanoBoost(plr) ? 3 : 0; int value = skill->values[0][boost]; BuffStack passiveBuff = { @@ -137,19 +215,18 @@ bool Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) { passiveBuff.buffStackClass = target == self ? BuffClass::NANO : BuffClass::GROUP_NANO; ICombatant* combatant = dynamic_cast(entity); - newBuffApplied |= combatant->addBuff(timeBuffId, + if(combatant->addBuff(timeBuffId, [](EntityRef self, Buff* buff, int status, BuffStack* stack) { Buffs::timeBuffUpdate(self, buff, status, stack); }, [](EntityRef self, Buff* buff, time_t currTime) { // TODO time buff tick }, - &passiveBuff); + &passiveBuff)) affected.push_back(combatant); } - return newBuffApplied; + return affected; } -#pragma endregion void Abilities::init() { //REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK, emailUpdateCheck); diff --git a/src/Abilities.hpp b/src/Abilities.hpp index 22be0c7..cc02b5e 100644 --- a/src/Abilities.hpp +++ b/src/Abilities.hpp @@ -1,11 +1,15 @@ #pragma once +#include "core/Core.hpp" + #include "Entities.hpp" #include "Player.hpp" #include #include +constexpr size_t MAX_SKILLRESULT_SIZE = sizeof(sSkillResult_BatteryDrain); + enum class SkillEffectTarget { POINT = 1, SELF = 2, @@ -26,6 +30,15 @@ enum class SkillDrainType { PASSIVE = 2 }; +struct SkillResult { + size_t size; + char payload[MAX_SKILLRESULT_SIZE]; + SkillResult(size_t len, void* dat) { + size = len; + memcpy(payload, dat, len); + } +}; + struct SkillData { int skillType; // eST SkillEffectTarget effectTarget; @@ -44,10 +57,11 @@ struct SkillData { namespace Abilities { extern std::map SkillTable; + void broadcastNanoSkill(CNSocket*, sNano&, std::vector); std::vector matchTargets(SkillData*, int, int32_t*); int getCSTBFromST(int eSkillType); - bool usePassiveNanoSkill(SkillData*, Player*, int); + std::vector usePassiveNanoSkill(SkillData*, Player*); void init(); } diff --git a/src/Combat.cpp b/src/Combat.cpp index 9b66305..9d9b887 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -19,6 +19,7 @@ using namespace Combat; /// Player Id -> Bullet Id -> Bullet std::map> Combat::Bullets; +#pragma region Player bool Player::addBuff(int buffId, BuffCallback onUpdate, BuffCallback onTick, BuffStack* stack) { EntityRef self = PlayerManager::getSockFromID(iID); @@ -87,6 +88,10 @@ std::vector Player::getGroupMembers() { return members; } +int32_t Player::getCharType() { + return 1; // eCharType (eCT_PC) +} + int32_t Player::getID() { return iID; } @@ -94,7 +99,9 @@ int32_t Player::getID() { void Player::step(time_t currTime) { // no-op } +#pragma endregion +#pragma region CombatNPC bool CombatNPC::addBuff(int buffId, BuffCallback onUpdate, BuffCallback onTick, BuffStack* stack) { /* stubbed */ return false; } @@ -143,6 +150,12 @@ std::vector CombatNPC::getGroupMembers() { return members; } +int32_t CombatNPC::getCharType() { + if(kind == EntityKind::MOB) + return 4; // eCharType (eCT_MOB) + return 2; // eCharType (eCT_NPC) +} + int32_t CombatNPC::getID() { return id; } @@ -172,6 +185,7 @@ void CombatNPC::transition(AIState newState, EntityRef src) { event.handler(src, this); */ } +#pragma endregion static std::pair getDamage(int attackPower, int defensePower, bool shouldCrit, bool batteryBoost, int attackerStyle, @@ -784,10 +798,7 @@ static void playerTick(CNServer *serv, time_t currTime) { if (skill->drainType == SkillDrainType::PASSIVE) { // apply passive buff drainRate = skill->batteryUse[boost * 3]; - if(Abilities::usePassiveNanoSkill(skill, plr, boost * 3)) { - // first buff, send back skill use packet(s) - // TODO - } + Abilities::usePassiveNanoSkill(skill, plr); } } diff --git a/src/Entities.hpp b/src/Entities.hpp index d6cd361..bba7bd8 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -57,6 +57,7 @@ public: virtual bool isAlive() = 0; virtual int getCurrentHP() = 0; virtual std::vector getGroupMembers() = 0; + virtual int32_t getCharType() = 0; virtual int32_t getID() = 0; virtual void step(time_t currTime) = 0; }; @@ -74,6 +75,7 @@ public: bool loopingPath = false; BaseNPC(int _A, uint64_t iID, int t, int _id) { + kind = EntityKind::SIMPLE_NPC; type = t; hp = 400; angle = _A; @@ -108,6 +110,8 @@ struct CombatNPC : public BaseNPC, public ICombatant { spawnY = y; spawnZ = z; + kind = EntityKind::COMBAT_NPC; + stateHandlers[AIState::INACTIVE] = {}; transitionHandlers[AIState::INACTIVE] = {}; } @@ -124,6 +128,7 @@ struct CombatNPC : public BaseNPC, public ICombatant { virtual bool isAlive() override; virtual int getCurrentHP() override; virtual std::vector getGroupMembers() override; + virtual int32_t getCharType() override; virtual int32_t getID() override; virtual void step(time_t currTime) override; diff --git a/src/Nanos.cpp b/src/Nanos.cpp index d5f83f9..de91c8b 100644 --- a/src/Nanos.cpp +++ b/src/Nanos.cpp @@ -86,10 +86,17 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) { return; // sanity check plr->activeNano = nanoID; + sNano& nano = plr->Nanos[nanoID]; - int16_t skillID = plr->Nanos[nanoID].iSkillID; - if (Abilities::SkillTable.count(skillID) > 0 && Abilities::SkillTable[skillID].drainType == SkillDrainType::PASSIVE) - resp.eCSTB___Add = 1; // passive buff effect + SkillData* skill = Abilities::SkillTable.count(nano.iSkillID) > 0 + ? &Abilities::SkillTable[nano.iSkillID] : nullptr; + if (slot != -1 && skill != nullptr && skill->drainType == SkillDrainType::PASSIVE) { + // passive buff effect + resp.eCSTB___Add = 1; + int boost = Nanos::getNanoBoost(plr); + std::vector affectedCombatants = Abilities::usePassiveNanoSkill(skill, plr); + if(!affectedCombatants.empty()) Abilities::broadcastNanoSkill(sock, nano, affectedCombatants); + } if (!silent) // silent nano death but only for the summoning player sock->sendPacket(resp, P_FE2CL_REP_NANO_ACTIVE_SUCC); @@ -97,7 +104,7 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) { // Send to other players, these players can't handle silent nano deaths so this packet needs to be sent. INITSTRUCT(sP_FE2CL_NANO_ACTIVE, pkt1); pkt1.iPC_ID = plr->iID; - pkt1.Nano = plr->Nanos[nanoID]; + pkt1.Nano = nano; PlayerManager::sendToViewable(sock, pkt1, P_FE2CL_NANO_ACTIVE); } diff --git a/src/Player.hpp b/src/Player.hpp index 7f65840..311b923 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -99,6 +99,7 @@ struct Player : public Entity, public ICombatant { virtual bool isAlive() override; virtual int getCurrentHP() override; virtual std::vector getGroupMembers() override; + virtual int32_t getCharType() override; virtual int32_t getID() override; virtual void step(time_t currTime) override;