From d9e0a4a281aa4b95ebd2a1a5d403c89000d531e8 Mon Sep 17 00:00:00 2001 From: gsemaj Date: Mon, 11 Apr 2022 23:14:03 -0400 Subject: [PATCH] (WIP) Start implementing ICombatant Start by replacing `hitMob` with `takeDamage` interface function. Simplify `pcAttackChars` a little by utilizing the new interface, then add more interface functions as needed. A lot of the combat logic is tied to the `Mob` class. Need to start moving stuff over to CombatNPC. --- src/Abilities.cpp | 8 +- src/Combat.cpp | 182 ++++++++++++++++++++++--------------------- src/Combat.hpp | 1 - src/Entities.hpp | 8 +- src/Player.hpp | 4 +- src/core/Packets.hpp | 2 +- 6 files changed, 106 insertions(+), 99 deletions(-) diff --git a/src/Abilities.cpp b/src/Abilities.cpp index cc613a4..a2ed2cf 100644 --- a/src/Abilities.cpp +++ b/src/Abilities.cpp @@ -148,7 +148,7 @@ bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t target } Mob* mob = (Mob*)npc; - Combat::hitMob(sock, mob, 0); + mob->takeDamage(sock, 0); respdata[i].eCT = 4; respdata[i].iID = mob->id; @@ -220,7 +220,7 @@ bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int Mob* mob = (Mob*)npc; - Combat::hitMob(sock, mob, 0); // just to gain aggro + mob->takeDamage(sock, 0); respdata[i].eCT = 4; respdata[i].iDamage = duration / 10; @@ -289,7 +289,7 @@ bool doDamage(CNSocket *sock, sSkillResult_Damage *respdata, int i, int32_t targ Mob* mob = (Mob*)npc; Player *plr = PlayerManager::getPlayer(sock); - int damage = Combat::hitMob(sock, mob, std::max(PC_MAXHEALTH(plr->level) * amount / 1000, mob->maxHealth * amount / 1000)); + int damage = mob->takeDamage(sock, std::max(PC_MAXHEALTH(plr->level) * amount / 1000, mob->maxHealth * amount / 1000)); respdata[i].eCT = 4; respdata[i].iDamage = damage; @@ -344,7 +344,7 @@ bool doLeech(CNSocket *sock, sSkillResult_Heal_HP *healdata, int i, int32_t targ Mob* mob = (Mob*)npc; - int damage = Combat::hitMob(sock, mob, amount * 2); + int damage = mob->takeDamage(sock, amount * 2); damagedata->eCT = 4; damagedata->iDamage = damage; diff --git a/src/Combat.cpp b/src/Combat.cpp index 25179c7..e58cf42 100644 --- a/src/Combat.cpp +++ b/src/Combat.cpp @@ -17,8 +17,9 @@ using namespace Combat; /// Player Id -> Bullet Id -> Bullet std::map> Combat::Bullets; -void Player::takeDamage(EntityRef src, int amt) { - // stubbed +int Player::takeDamage(EntityRef src, int amt) { + HP -= amt; + return amt; } void Player::heal(EntityRef src, int amt) { @@ -29,8 +30,54 @@ bool Player::isAlive() { return HP > 0; } -void CombatNPC::takeDamage(EntityRef src, int amt) { - // stubbed +int Player::getCurrentHP() { + return HP; +} + +int32_t Player::getID() { + return iID; +} + +int CombatNPC::takeDamage(EntityRef src, int amt) { + + /* REFACTOR: all of this logic is strongly coupled to mobs. + * come back to this when more of it is moved to CombatNPC. + * remove this cast when done */ + Mob* mob = (Mob*)this; + + // cannot kill mobs multiple times; cannot harm retreating mobs + if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) { + return 0; // no damage + } + + if (mob->skillStyle >= 0) + return 0; // don't hurt a mob casting corruption + + if (mob->state == MobState::ROAMING) { + assert(mob->target == nullptr && src.type == EntityType::PLAYER); // players only for now + MobAI::enterCombat(src.sock, mob); + + if (mob->groupLeader != 0) + MobAI::followToCombat(mob); + } + + hp -= amt; + + // wake up sleeping monster + if (mob->cbf & CSB_BIT_MEZ) { + mob->cbf &= ~CSB_BIT_MEZ; + + INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); + pkt1.eCT = 2; + pkt1.iID = mob->id; + pkt1.iConditionBitFlag = mob->cbf; + NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); + } + + if (mob->hp <= 0) + killMob(mob->target, mob); + + return amt; } void CombatNPC::heal(EntityRef src, int amt) { @@ -41,6 +88,14 @@ bool CombatNPC::isAlive() { return hp > 0; } +int CombatNPC::getCurrentHP() { + return hp; +} + +int32_t CombatNPC::getID() { + return id; +} + static std::pair getDamage(int attackPower, int defensePower, bool shouldCrit, bool batteryBoost, int attackerStyle, int defenderStyle, int difficulty) { @@ -161,7 +216,7 @@ static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) { else plr->batteryW = 0; - damage.first = hitMob(sock, mob, damage.first); + damage.first = mob->takeDamage(sock, damage.first); respdata[i].iID = mob->id; respdata[i].iDamage = damage.first; @@ -214,42 +269,6 @@ void Combat::npcAttackPc(Mob *mob, time_t currTime) { } } -int Combat::hitMob(CNSocket *sock, Mob *mob, int damage) { - // cannot kill mobs multiple times; cannot harm retreating mobs - if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) { - return 0; // no damage - } - - if (mob->skillStyle >= 0) - return 0; // don't hurt a mob casting corruption - - if (mob->state == MobState::ROAMING) { - assert(mob->target == nullptr); - MobAI::enterCombat(sock, mob); - - if (mob->groupLeader != 0) - MobAI::followToCombat(mob); - } - - mob->hp -= damage; - - // wake up sleeping monster - if (mob->cbf & CSB_BIT_MEZ) { - mob->cbf &= ~CSB_BIT_MEZ; - - INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1); - pkt1.eCT = 2; - pkt1.iID = mob->id; - pkt1.iConditionBitFlag = mob->cbf; - NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT)); - } - - if (mob->hp <= 0) - killMob(mob->target, mob); - - return damage; -} - /* * When a group of players is doing missions together, we want them to all get * quest items at the same time, but we don't want the odds of quest item @@ -441,7 +460,7 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { return; // Unlike the attack mob packet, attacking players packet has an 8-byte trail (Instead of 4 bytes). - if (!validInVarPacket(sizeof(sP_CL2FE_REQ_PC_ATTACK_CHARs), pkt->iTargetCnt, sizeof(int32_t) * 2, data->size)) { + if (!validInVarPacket(sizeof(sP_CL2FE_REQ_PC_ATTACK_CHARs), pkt->iTargetCnt, sizeof(sGM_PVPTarget), data->size)) { std::cout << "[WARN] bad sP_CL2FE_REQ_PC_ATTACK_CHARs packet size\n"; return; } @@ -465,11 +484,20 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { resp->iTargetCnt = pkt->iTargetCnt; for (int i = 0; i < pkt->iTargetCnt; i++) { - if (pktdata[i*2+1] == 1) { // eCT == 1; attack player - Player *target = nullptr; + + ICombatant* target = nullptr; + sGM_PVPTarget* targdata = (sGM_PVPTarget*)(pktdata + i * 2); + std::pair damage; + + if (pkt->iTargetCnt > 1) + damage.first = plr->groupDamage; + else + damage.first = plr->pointDamage; + + if (targdata->eCT == 1) { // eCT == 1; attack player for (auto& pair : PlayerManager::players) { - if (pair.second->iID == pktdata[i*2]) { + if (pair.second->iID == targdata->iID) { target = pair.second; break; } @@ -481,67 +509,41 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) { return; } - std::pair damage; + damage = getDamage(damage.first, ((Player*)target)->defense, true, (plr->batteryW > 6 + plr->level), -1, -1, 0); - if (pkt->iTargetCnt > 1) - damage.first = plr->groupDamage; - else - damage.first = plr->pointDamage; - - damage = getDamage(damage.first, target->defense, true, (plr->batteryW > 6 + plr->level), -1, -1, 0); - - if (plr->batteryW >= 6 + plr->level) - plr->batteryW -= 6 + plr->level; - else - plr->batteryW = 0; - - target->HP -= damage.first; - - respdata[i].eCT = pktdata[i*2+1]; - respdata[i].iID = target->iID; - respdata[i].iDamage = damage.first; - respdata[i].iHP = target->HP; - respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } else { // eCT == 4; attack mob - if (NPCManager::NPCs.find(pktdata[i*2]) == NPCManager::NPCs.end()) { + + if (NPCManager::NPCs.find(targdata->iID) == NPCManager::NPCs.end()) { // not sure how to best handle this std::cout << "[WARN] pcAttackChars: NPC ID not found" << std::endl; return; } - BaseNPC* npc = NPCManager::NPCs[pktdata[i * 2]]; + BaseNPC* npc = NPCManager::NPCs[targdata->iID]; if (npc->kind != EntityType::MOB) { std::cout << "[WARN] pcAttackChars: NPC is not a mob" << std::endl; return; } Mob* mob = (Mob*)npc; - - std::pair damage; - - if (pkt->iTargetCnt > 1) - damage.first = plr->groupDamage; - else - damage.first = plr->pointDamage; - + target = mob; int difficulty = (int)mob->data["m_iNpcLevel"]; - damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW > 6 + difficulty), Nanos::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty); - - if (plr->batteryW >= 6 + difficulty) - plr->batteryW -= 6 + difficulty; - else - plr->batteryW = 0; - - damage.first = hitMob(sock, mob, damage.first); - - respdata[i].eCT = pktdata[i*2+1]; - respdata[i].iID = mob->id; - respdata[i].iDamage = damage.first; - respdata[i].iHP = mob->hp; - respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } + + if (plr->batteryW >= 6 + plr->level) + plr->batteryW -= 6 + plr->level; + else + plr->batteryW = 0; + + damage.first = target->takeDamage(sock, damage.first); + + respdata[i].eCT = targdata->eCT; + respdata[i].iID = target->getID(); + respdata[i].iDamage = damage.first; + respdata[i].iHP = target->getCurrentHP(); + respdata[i].iHitFlag = damage.second; // hitscan, not a rocket or a grenade } sock->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_CHARs_SUCC, resplen); @@ -733,7 +735,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) { int difficulty = (int)mob->data["m_iNpcLevel"]; damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, bullet->weaponBoost, Nanos::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty); - damage.first = hitMob(sock, mob, damage.first); + damage.first = mob->takeDamage(sock, damage.first); respdata[i].iID = mob->id; respdata[i].iDamage = damage.first; diff --git a/src/Combat.hpp b/src/Combat.hpp index 89863d1..bb06e54 100644 --- a/src/Combat.hpp +++ b/src/Combat.hpp @@ -23,6 +23,5 @@ namespace Combat { void init(); void npcAttackPc(Mob *mob, time_t currTime); - int hitMob(CNSocket *sock, Mob *mob, int damage); void killMob(CNSocket *sock, Mob *mob); } diff --git a/src/Entities.hpp b/src/Entities.hpp index 1671727..0db6fb1 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -78,9 +78,11 @@ public: ICombatant() {} virtual ~ICombatant() {} - virtual void takeDamage(EntityRef, int) = 0; + virtual int takeDamage(EntityRef, int) = 0; virtual void heal(EntityRef, int) = 0; virtual bool isAlive() = 0; + virtual int getCurrentHP() = 0; + virtual int32_t getID() = 0; }; /* @@ -136,9 +138,11 @@ struct CombatNPC : public BaseNPC, public ICombatant { virtual bool isExtant() override { return hp > 0; } - virtual void takeDamage(EntityRef src, int amt) override; + virtual int takeDamage(EntityRef src, int amt) override; virtual void heal(EntityRef src, int amt) override; virtual bool isAlive() override; + virtual int getCurrentHP() override; + virtual int32_t getID() override; }; // Mob is in MobAI.hpp, Player is in Player.hpp diff --git a/src/Player.hpp b/src/Player.hpp index 6fb451e..845b2c0 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -90,9 +90,11 @@ struct Player : public Entity, public ICombatant { virtual void enterIntoViewOf(CNSocket *sock) override; virtual void disappearFromViewOf(CNSocket *sock) override; - virtual void takeDamage(EntityRef src, int amt) override; + virtual int takeDamage(EntityRef src, int amt) override; virtual void heal(EntityRef src, int amt) override; virtual bool isAlive() override; + virtual int getCurrentHP() override; + virtual int32_t getID() override; sPCAppearanceData getAppearanceData(); }; diff --git a/src/core/Packets.hpp b/src/core/Packets.hpp index 55a0358..f79bb2a 100644 --- a/src/core/Packets.hpp +++ b/src/core/Packets.hpp @@ -47,8 +47,8 @@ struct PacketDesc { * really should. */ struct sGM_PVPTarget { - uint32_t eCT; uint32_t iID; + uint32_t eCT; }; struct sSkillResult_Leech {