From ab990116a26fbed67725e10c86111e47b8452fd7 Mon Sep 17 00:00:00 2001 From: Jade Date: Tue, 29 Sep 2020 22:27:48 +0100 Subject: [PATCH] Various bugfixes and adjustments * Nano missions should now stop repeating. * Bitwise operators are now used to handle buff/debuff bitfields. * Changing nano powers will no longer grant you infinite buffs. * Mobs now heal up client-side after retreating, this comes with candy effect being played however. * Lower level mobs now hit harder. * Nanos drain stamina quicker when they grant passive powers. * Healing, damage and leech powers scale up with your level. * Player on player damage now accounts for damage and armor. --- src/MissionManager.cpp | 5 +-- src/MobManager.cpp | 71 +++++++++++++++++++++++++++++++----------- src/MobManager.hpp | 1 + src/NanoManager.cpp | 63 +++++++++++++++++++++++-------------- src/Player.hpp | 1 + 5 files changed, 96 insertions(+), 45 deletions(-) diff --git a/src/MissionManager.cpp b/src/MissionManager.cpp index 07a7ff9..9b9659a 100644 --- a/src/MissionManager.cpp +++ b/src/MissionManager.cpp @@ -167,11 +167,8 @@ bool MissionManager::endTask(CNSocket *sock, int32_t taskNum) { saveMission(plr, (int)(task["m_iHMissionID"])-1); // if it's a nano mission, reward the nano. - if (task["m_iSTNanoID"] != 0) { + if (task["m_iSTNanoID"] != 0) NanoManager::addNano(sock, task["m_iSTNanoID"], 0, true); - // check if the player already has enough fm for the next mission - updateFusionMatter(sock, 0); - } // remove current mission plr->CurrentMissionID = 0; diff --git a/src/MobManager.cpp b/src/MobManager.cpp index 35ddd84..dd33502 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -114,7 +114,7 @@ void MobManager::npcAttackPc(Mob *mob) { sP_FE2CL_NPC_ATTACK_PCs *pkt = (sP_FE2CL_NPC_ATTACK_PCs*)respbuf; sAttackResult *atk = (sAttackResult*)(respbuf + sizeof(sP_FE2CL_NPC_ATTACK_PCs)); - auto damage = getDamage(440 + (int)mob->data["m_iPower"], plr->defense, false, 36 - (int)mob->data["m_iNpcLevel"]); + auto damage = getDamage(470 + (int)mob->data["m_iPower"], plr->defense, false, 1); plr->HP -= damage.first; pkt->iNPC_ID = mob->appearanceData.iNPC_ID; @@ -443,6 +443,11 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) { mob->killedTime = 0; mob->nextAttack = 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); } } @@ -489,19 +494,21 @@ void MobManager::step(CNServer *serv, time_t currTime) { * only returns the first step, since the rest will need to be recalculated anyway if chasing player. */ std::pair MobManager::lerp(int x1, int y1, int x2, int y2, int speed) { - std::pair ret = {}; + std::pair ret = {x1, y1}; speed /= 2; + + if (speed == 0) + return ret; + int distance = hypot(x1 - x2, y1 - y2); if (distance > speed) { - if (speed == 0) - speed = 1; int lerps = distance / speed; // interpolate only the first point - float frac = 1.0f / (lerps); + float frac = 1.0f / lerps; ret.first = (x1 + (x2 - x1) * frac); ret.second = (y1 + (y2 - y1) * frac); @@ -579,15 +586,11 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) { // fm patch/lake damage if (plr->dotDamage) - dealGooDamage(sock, 150); - - // a somewhat hacky way tick goo damage faster than heal, but eh - if (currTime - lastHealTime < 4000) - continue; + dealGooDamage(sock, PC_MAXHEALTH(plr->level) * 3 / 20); // heal - if (!plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) { - plr->HP += 200; + if (currTime - lastHealTime >= 6000 && !plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) { + plr->HP += PC_MAXHEALTH(plr->level) / 5; if (plr->HP > PC_MAXHEALTH(plr->level)) plr->HP = PC_MAXHEALTH(plr->level); transmit = true; @@ -595,7 +598,10 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) { for (int i = 0; i < 3; i++) { if (plr->activeNano != 0 && plr->equippedNanos[i] == plr->activeNano) { // spend stamina - plr->Nanos[plr->activeNano].iStamina -= 3; + plr->Nanos[plr->activeNano].iStamina -= 1; + + if (plr->passiveNanoOut) + plr->Nanos[plr->activeNano].iStamina -= 1; if (plr->Nanos[plr->activeNano].iStamina < 0) plr->activeNano = 0; @@ -603,7 +609,7 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) { transmit = true; } else if (plr->Nanos[plr->equippedNanos[i]].iStamina < 150) { // regain stamina sNano& nano = plr->Nanos[plr->equippedNanos[i]]; - nano.iStamina += 3; + nano.iStamina += 1; if (nano.iStamina > 150) nano.iStamina = 150; @@ -627,7 +633,7 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) { } // if this was a heal tick, update the counter outside of the loop - if (currTime - lastHealTime < 4000) + if (currTime - lastHealTime >= 6000) lastHealTime = currTime; } @@ -694,14 +700,21 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) { std::cout << "[WARN] pcAttackChars: player ID not found" << std::endl; return; } + + std::pair damage; + + if (pkt->iTargetCnt > 1) + damage = getDamage(plr->groupDamage, target->defense, true, 1); + else + damage = getDamage(plr->pointDamage, target->defense, true, 1); - target->HP -= 700; + target->HP -= damage.first; respdata[i].eCT = pktdata[i*2+1]; respdata[i].iID = target->iID; - respdata[i].iDamage = 700; + respdata[i].iDamage = damage.first; respdata[i].iHP = target->HP; - respdata[i].iHitFlag = 2; // hitscan, not a rocket or a grenade + respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } else { // eCT == 4; attack mob if (Mobs.find(pktdata[i*2]) == Mobs.end()) { // not sure how to best handle this @@ -738,3 +751,25 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) { // send to other players PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_CHARs, resplen); } + +void MobManager::resendMobHP(Mob *mob) { + size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Heal_HP); + assert(resplen < CN_PACKET_BUFFER_SIZE - 8); + 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_Heal_HP *heal = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK)); + + pkt->iID = mob->appearanceData.iNPC_ID; + pkt->eCT = 4; // mob + pkt->iTB_ID = ECSB_HEAL; // sSkillResult_Heal_HP + + heal->eCT = 4; + heal->iID = mob->appearanceData.iNPC_ID; + heal->iHealHP = 0; + heal->iHP = mob->appearanceData.iHP; + + NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); +} \ No newline at end of file diff --git a/src/MobManager.hpp b/src/MobManager.hpp index 60b3594..879d273 100644 --- a/src/MobManager.hpp +++ b/src/MobManager.hpp @@ -107,4 +107,5 @@ namespace MobManager { std::pair getDamage(int, int, bool, int); void pcAttackChars(CNSocket *sock, CNPacketData *data); + void resendMobHP(Mob *mob); } diff --git a/src/NanoManager.cpp b/src/NanoManager.cpp index c024aed..1a5f1ee 100644 --- a/src/NanoManager.cpp +++ b/src/NanoManager.cpp @@ -230,8 +230,10 @@ void NanoManager::addNano(CNSocket* sock, int16_t nanoId, int16_t slot, bool spe * Note the use of the not-yet-incremented plr->level as opposed to level. * Doing it the other way always leaves the FM at 0. Jade totally called it. */ + plr->level = level; + if (spendfm) - MissionManager::updateFusionMatter(sock, -(int)MissionManager::AvatarGrowth[plr->level]["m_iReqBlob_NanoCreate"]); + MissionManager::updateFusionMatter(sock, -(int)MissionManager::AvatarGrowth[plr->level-1]["m_iReqBlob_NanoCreate"]); // Send to client INITSTRUCT(sP_FE2CL_REP_PC_NANO_CREATE_SUCC, resp); @@ -241,10 +243,11 @@ void NanoManager::addNano(CNSocket* sock, int16_t nanoId, int16_t slot, bool spe resp.iPC_Level = level; resp.iPC_FusionMatter = plr->fusionmatter; + if (plr->activeNano > 0 && plr->activeNano == nanoId) + summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs + // Update player plr->Nanos[nanoId] = resp.Nano; - plr->level = level; - plr->iConditionBitFlag = 0; sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_NANO_CREATE_SUCC, sizeof(sP_FE2CL_REP_PC_NANO_CREATE_SUCC)); @@ -281,8 +284,10 @@ void NanoManager::summonNano(CNSocket *sock, int slot) { if (plr->activeNano > 0) 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); + plr->passiveNanoOut = false; + } sNano nano = plr->Nanos[nanoId]; skillId = nano.iSkillID; @@ -294,6 +299,7 @@ void NanoManager::summonNano(CNSocket *sock, int slot) { if (pwr.powers.count(skillId)) { // std::set's contains method is C++20 only... resp.eCSTB___Add = 1; nanoBuff(sock, nanoId, skillId, pwr.eSkillType, pwr.iCBFlag, pwr.eCharStatusTimeBuffID, pwr.iValue); + plr->passiveNanoOut = true; } } else plr->activeNano = 0; @@ -325,6 +331,9 @@ void NanoManager::setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId) if (plr == nullptr) return; + if (plr->activeNano > 0 && plr->activeNano == nanoId) + summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs + sNano nano = plr->Nanos[nanoId]; nano.iSkillID = skillId; @@ -417,16 +426,18 @@ bool doHeal(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *respdata, in // player not found if (plr == nullptr) return false; - - if (plr->HP + amount > PC_MAXHEALTH(plr->level)) + + int healedAmount = PC_MAXHEALTH(plr->level) * amount / 100; + + plr->HP += healedAmount; + + if (plr->HP > PC_MAXHEALTH(plr->level)) plr->HP = PC_MAXHEALTH(plr->level); - else - plr->HP += amount; respdata[i].eCT = 1; respdata[i].iID = plr->iID; respdata[i].iHP = plr->HP; - respdata[i].iHealHP = amount; + respdata[i].iHealHP = healedAmount; std::cout << (int)plr->iID << " was healed" << std::endl; @@ -441,7 +452,12 @@ bool doDamage(CNSocket *sock, int32_t *pktdata, sSkillResult_Damage *respdata, i } Mob* mob = MobManager::Mobs[pktdata[i]]; - int damage = MobManager::hitMob(sock, mob, amount); + Player *plr = PlayerManager::getPlayer(sock); + + if (plr == nullptr) + return false; + + int damage = MobManager::hitMob(sock, mob, PC_MAXHEALTH(plr->level) * amount / 100); respdata[i].eCT = 4; respdata[i].iDamage = damage; @@ -474,15 +490,17 @@ bool doLeech(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *healdata, i if (plr == nullptr) return false; - if (plr->HP + amount > PC_MAXHEALTH(plr->level)) + int healedAmount = PC_MAXHEALTH(plr->level) * amount / 100; + + plr->HP += healedAmount; + + if (plr->HP > PC_MAXHEALTH(plr->level)) plr->HP = PC_MAXHEALTH(plr->level); - else - plr->HP += amount; healdata->eCT = 1; healdata->iID = plr->iID; healdata->iHP = plr->HP; - healdata->iHealHP = amount; + healdata->iHealHP = healedAmount; if (MobManager::Mobs.find(pktdata[i]) == MobManager::Mobs.end()) { // not sure how to best handle this @@ -491,7 +509,7 @@ bool doLeech(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *healdata, i } Mob* mob = MobManager::Mobs[pktdata[i]]; - int damage = MobManager::hitMob(sock, mob, amount); + int damage = MobManager::hitMob(sock, mob, PC_MAXHEALTH(plr->level) * amount / 100); damagedata->eCT = 4; damagedata->iDamage = damage; @@ -567,13 +585,13 @@ void activePower(CNSocket *sock, CNPacketData *data, // active nano power dispatch table std::vector ActivePowers = { ActivePower(StunPowers, activePower, EST_STUN, CSB_BIT_STUN, 0), - ActivePower(HealPowers, activePower, EST_HEAL_HP, CSB_BIT_NONE, 333), + ActivePower(HealPowers, activePower, EST_HEAL_HP, CSB_BIT_NONE, 25), // TODO: Recall ActivePower(DrainPowers, activePower, EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, 0), ActivePower(SnarePowers, activePower, EST_SNARE, CSB_BIT_DN_MOVE_SPEED, 0), - ActivePower(DamagePowers, activePower, EST_DAMAGE, CSB_BIT_NONE, 133), + ActivePower(DamagePowers, activePower, EST_DAMAGE, CSB_BIT_NONE, 12), // TODO: GroupRevive - ActivePower(LeechPowers, activePower, EST_BLOODSUCKING, CSB_BIT_NONE, 133), + ActivePower(LeechPowers, activePower, EST_BLOODSUCKING, CSB_BIT_NONE, 18), ActivePower(SleepPowers, activePower, EST_SLEEP, CSB_BIT_MEZ, 0), }; @@ -610,7 +628,8 @@ void NanoManager::nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t // this looks stupid but in the future there will be more counts (for group powers) for (int i = 0; i < 1; i++) { - plr->iConditionBitFlag += iCBFlag; + if (!(plr->iConditionBitFlag & iCBFlag)) + plr->iConditionBitFlag ^= iCBFlag; respdata[i].eCT = 1; respdata[i].iID = plr->iID; @@ -641,10 +660,8 @@ void NanoManager::nanoUnbuff(CNSocket* sock, int32_t iCBFlag, int16_t eCharStatu if (plr == nullptr) return; - if (iCBFlag < plr->iConditionBitFlag) // prevents integer underflow - plr->iConditionBitFlag -= iCBFlag; - else - plr->iConditionBitFlag = 0; + if (plr->iConditionBitFlag & iCBFlag) + plr->iConditionBitFlag ^= iCBFlag; resp1.eCSTB = eCharStatusTimeBuffID; //eCharStatusTimeBuffID resp1.eTBU = 2; //eTimeBuffUpdate diff --git a/src/Player.hpp b/src/Player.hpp index 2726482..e882bf7 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -47,6 +47,7 @@ struct Player { bool inCombat; bool dotDamage; + bool passiveNanoOut; int pointDamage; int groupDamage;