mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 13:30:06 +00:00
(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.
This commit is contained in:
parent
166e148878
commit
5d9dcb8609
@ -148,7 +148,7 @@ bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t target
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mob* mob = (Mob*)npc;
|
Mob* mob = (Mob*)npc;
|
||||||
Combat::hitMob(sock, mob, 0);
|
mob->takeDamage(sock, 0);
|
||||||
|
|
||||||
respdata[i].eCT = 4;
|
respdata[i].eCT = 4;
|
||||||
respdata[i].iID = mob->id;
|
respdata[i].iID = mob->id;
|
||||||
@ -220,7 +220,7 @@ bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int
|
|||||||
|
|
||||||
Mob* mob = (Mob*)npc;
|
Mob* mob = (Mob*)npc;
|
||||||
|
|
||||||
Combat::hitMob(sock, mob, 0); // just to gain aggro
|
mob->takeDamage(sock, 0);
|
||||||
|
|
||||||
respdata[i].eCT = 4;
|
respdata[i].eCT = 4;
|
||||||
respdata[i].iDamage = duration / 10;
|
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;
|
Mob* mob = (Mob*)npc;
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
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].eCT = 4;
|
||||||
respdata[i].iDamage = damage;
|
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;
|
Mob* mob = (Mob*)npc;
|
||||||
|
|
||||||
int damage = Combat::hitMob(sock, mob, amount * 2);
|
int damage = mob->takeDamage(sock, amount * 2);
|
||||||
|
|
||||||
damagedata->eCT = 4;
|
damagedata->eCT = 4;
|
||||||
damagedata->iDamage = damage;
|
damagedata->iDamage = damage;
|
||||||
|
182
src/Combat.cpp
182
src/Combat.cpp
@ -17,8 +17,9 @@ using namespace Combat;
|
|||||||
/// Player Id -> Bullet Id -> Bullet
|
/// Player Id -> Bullet Id -> Bullet
|
||||||
std::map<int32_t, std::map<int8_t, Bullet>> Combat::Bullets;
|
std::map<int32_t, std::map<int8_t, Bullet>> Combat::Bullets;
|
||||||
|
|
||||||
void Player::takeDamage(EntityRef src, int amt) {
|
int Player::takeDamage(EntityRef src, int amt) {
|
||||||
// stubbed
|
HP -= amt;
|
||||||
|
return amt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::heal(EntityRef src, int amt) {
|
void Player::heal(EntityRef src, int amt) {
|
||||||
@ -29,8 +30,54 @@ bool Player::isAlive() {
|
|||||||
return HP > 0;
|
return HP > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CombatNPC::takeDamage(EntityRef src, int amt) {
|
int Player::getCurrentHP() {
|
||||||
// stubbed
|
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) {
|
void CombatNPC::heal(EntityRef src, int amt) {
|
||||||
@ -41,6 +88,14 @@ bool CombatNPC::isAlive() {
|
|||||||
return hp > 0;
|
return hp > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CombatNPC::getCurrentHP() {
|
||||||
|
return hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t CombatNPC::getID() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shouldCrit,
|
static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shouldCrit,
|
||||||
bool batteryBoost, int attackerStyle,
|
bool batteryBoost, int attackerStyle,
|
||||||
int defenderStyle, int difficulty) {
|
int defenderStyle, int difficulty) {
|
||||||
@ -152,7 +207,7 @@ static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
|||||||
else
|
else
|
||||||
plr->batteryW = 0;
|
plr->batteryW = 0;
|
||||||
|
|
||||||
damage.first = hitMob(sock, mob, damage.first);
|
damage.first = mob->takeDamage(sock, damage.first);
|
||||||
|
|
||||||
respdata[i].iID = mob->id;
|
respdata[i].iID = mob->id;
|
||||||
respdata[i].iDamage = damage.first;
|
respdata[i].iDamage = damage.first;
|
||||||
@ -205,42 +260,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
|
* 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
|
* quest items at the same time, but we don't want the odds of quest item
|
||||||
@ -432,7 +451,7 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Unlike the attack mob packet, attacking players packet has an 8-byte trail (Instead of 4 bytes).
|
// 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";
|
std::cout << "[WARN] bad sP_CL2FE_REQ_PC_ATTACK_CHARs packet size\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -456,11 +475,20 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) {
|
|||||||
resp->iTargetCnt = pkt->iTargetCnt;
|
resp->iTargetCnt = pkt->iTargetCnt;
|
||||||
|
|
||||||
for (int i = 0; i < pkt->iTargetCnt; i++) {
|
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<int, int> 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) {
|
for (auto& pair : PlayerManager::players) {
|
||||||
if (pair.second->iID == pktdata[i*2]) {
|
if (pair.second->iID == targdata->iID) {
|
||||||
target = pair.second;
|
target = pair.second;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -472,67 +500,41 @@ static void pcAttackChars(CNSocket *sock, CNPacketData *data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int,int> 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
|
} 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
|
// not sure how to best handle this
|
||||||
std::cout << "[WARN] pcAttackChars: NPC ID not found" << std::endl;
|
std::cout << "[WARN] pcAttackChars: NPC ID not found" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseNPC* npc = NPCManager::NPCs[pktdata[i * 2]];
|
BaseNPC* npc = NPCManager::NPCs[targdata->iID];
|
||||||
if (npc->kind != EntityType::MOB) {
|
if (npc->kind != EntityType::MOB) {
|
||||||
std::cout << "[WARN] pcAttackChars: NPC is not a mob" << std::endl;
|
std::cout << "[WARN] pcAttackChars: NPC is not a mob" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mob* mob = (Mob*)npc;
|
Mob* mob = (Mob*)npc;
|
||||||
|
target = mob;
|
||||||
std::pair<int,int> damage;
|
|
||||||
|
|
||||||
if (pkt->iTargetCnt > 1)
|
|
||||||
damage.first = plr->groupDamage;
|
|
||||||
else
|
|
||||||
damage.first = plr->pointDamage;
|
|
||||||
|
|
||||||
int difficulty = (int)mob->data["m_iNpcLevel"];
|
int difficulty = (int)mob->data["m_iNpcLevel"];
|
||||||
|
|
||||||
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW > 6 + difficulty),
|
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW > 6 + difficulty),
|
||||||
Nanos::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], 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);
|
sock->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_CHARs_SUCC, resplen);
|
||||||
@ -724,7 +726,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) {
|
|||||||
int difficulty = (int)mob->data["m_iNpcLevel"];
|
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 = 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].iID = mob->id;
|
||||||
respdata[i].iDamage = damage.first;
|
respdata[i].iDamage = damage.first;
|
||||||
|
@ -23,6 +23,5 @@ namespace Combat {
|
|||||||
void init();
|
void init();
|
||||||
|
|
||||||
void npcAttackPc(Mob *mob, time_t currTime);
|
void npcAttackPc(Mob *mob, time_t currTime);
|
||||||
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
|
||||||
void killMob(CNSocket *sock, Mob *mob);
|
void killMob(CNSocket *sock, Mob *mob);
|
||||||
}
|
}
|
||||||
|
@ -78,9 +78,11 @@ public:
|
|||||||
ICombatant() {}
|
ICombatant() {}
|
||||||
virtual ~ICombatant() {}
|
virtual ~ICombatant() {}
|
||||||
|
|
||||||
virtual void takeDamage(EntityRef, int) = 0;
|
virtual int takeDamage(EntityRef, int) = 0;
|
||||||
virtual void heal(EntityRef, int) = 0;
|
virtual void heal(EntityRef, int) = 0;
|
||||||
virtual bool isAlive() = 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 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 void heal(EntityRef src, int amt) override;
|
||||||
virtual bool isAlive() override;
|
virtual bool isAlive() override;
|
||||||
|
virtual int getCurrentHP() override;
|
||||||
|
virtual int32_t getID() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mob is in MobAI.hpp, Player is in Player.hpp
|
// Mob is in MobAI.hpp, Player is in Player.hpp
|
||||||
|
@ -90,9 +90,11 @@ struct Player : public Entity, public ICombatant {
|
|||||||
virtual void enterIntoViewOf(CNSocket *sock) override;
|
virtual void enterIntoViewOf(CNSocket *sock) override;
|
||||||
virtual void disappearFromViewOf(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 void heal(EntityRef src, int amt) override;
|
||||||
virtual bool isAlive() override;
|
virtual bool isAlive() override;
|
||||||
|
virtual int getCurrentHP() override;
|
||||||
|
virtual int32_t getID() override;
|
||||||
|
|
||||||
sPCAppearanceData getAppearanceData();
|
sPCAppearanceData getAppearanceData();
|
||||||
};
|
};
|
||||||
|
@ -47,8 +47,8 @@ struct PacketDesc {
|
|||||||
* really should.
|
* really should.
|
||||||
*/
|
*/
|
||||||
struct sGM_PVPTarget {
|
struct sGM_PVPTarget {
|
||||||
uint32_t eCT;
|
|
||||||
uint32_t iID;
|
uint32_t iID;
|
||||||
|
uint32_t eCT;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sSkillResult_Leech {
|
struct sSkillResult_Leech {
|
||||||
|
Loading…
Reference in New Issue
Block a user