More skill handlers

Note: need to revisit these when active powers are implemented to make
sure they are correct. DamageNDebuff isn't even implemented yet.
This commit is contained in:
gsemaj 2022-07-25 23:01:20 -07:00
parent 723e455b1d
commit 9977907842
7 changed files with 218 additions and 43 deletions

View File

@ -12,31 +12,127 @@ using namespace Abilities;
std::map<int32_t, SkillData> Abilities::SkillTable;
#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;
static SkillResult handleSkillDamage(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
EntityRef sourceRef = source->getRef();
double scalingFactor = 1;
if(sourceRef.kind == EntityKind::PLAYER)
scalingFactor = std::max(source->getMaxHP(), target->getMaxHP()) / 1000.0;
else
scalingFactor = source->getMaxHP() / 1500.0;
int damage = (int)(skill->values[0][power] * scalingFactor);
int dealt = target->takeDamage(sourceRef, damage);
sSkillResult_Damage result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.bProtected = dealt <= 0;
result.iDamage = dealt;
result.iHP = target->getCurrentHP();
return SkillResult(sizeof(sSkillResult_Damage), &result);
}
static SkillResult handleSkillHealHP(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
EntityRef sourceRef = source->getRef();
int heal = skill->values[0][power];
int healed = target->heal(sourceRef, heal);
sSkillResult_Heal_HP result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.iHealHP = heal;
result.iHP = target->getCurrentHP();
return SkillResult(sizeof(sSkillResult_Heal_HP), &result);
}
static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
// TODO abilities
sSkillResult_Damage_N_Debuff result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.bProtected = false;
result.iConditionBitFlag = target->getCompositeCondition();
return SkillResult(sizeof(sSkillResult_Damage_N_Debuff), &result);
}
static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
sSkillResult_Buff result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.bProtected = false;
result.iConditionBitFlag = target->getCompositeCondition();
return SkillResult(sizeof(sSkillResult_Buff), &result);
}
static SkillResult handleSkillBatteryDrain(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
if(target->getCharType() != 1)
return SkillResult(); // only Players are valid targets for battery drain
Player* plr = dynamic_cast<Player*>(target);
const double scalingFactor = (18 + source->getLevel()) / 36.0;
int boostDrain = (int)(skill->values[0][power] * scalingFactor);
if(boostDrain > plr->batteryW) boostDrain = plr->batteryW;
plr->batteryW -= boostDrain;
int potionDrain = (int)(skill->values[1][power] * scalingFactor);
if(potionDrain > plr->batteryN) potionDrain = plr->batteryN;
plr->batteryN -= potionDrain;
sSkillResult_BatteryDrain result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.bProtected = target->hasBuff(ECSB_PROTECT_BATTERY);
result.iDrainW = boostDrain;
result.iBatteryW = plr->batteryW;
result.iDrainN = potionDrain;
result.iBatteryN = plr->batteryN;
result.iStamina = plr->getActiveNano()->iStamina;
result.bNanoDeactive = plr->getActiveNano()->iStamina <= 0;
result.iConditionBitFlag = target->getCompositeCondition();
return SkillResult(sizeof(sSkillResult_BatteryDrain), &result);
}
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<Player*>(source);
sSkillResult_Move result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.iMapNum = plr->recallInstance;
result.iMoveX = plr->recallX;
result.iMoveY = plr->recallY;
result.iMoveZ = plr->recallZ;
return SkillResult(sizeof(sSkillResult_Move), &result);
}
static SkillResult handleSkillResurrect(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
sSkillResult_Resurrect result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.iRegenHP = target->getCurrentHP();
return SkillResult(sizeof(sSkillResult_Resurrect), &result);
}
#pragma endregion
void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICombatant*> affected) {
if(SkillTable.count(nano.iSkillID) == 0)
return;
SkillData* skill = &SkillTable[nano.iSkillID];
Player* plr = PlayerManager::getPlayer(sock);
int count = affected.size();
static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombatant* src, std::vector<ICombatant*> targets) {
size_t resultSize = 0;
SkillResult (*skillHandler)(SkillData*, ICombatant*) = nullptr;
SkillResult (*skillHandler)(SkillData*, int, ICombatant*, ICombatant*) = nullptr;
std::vector<SkillResult> results;
switch(skill->skillType)
{
case EST_DAMAGE:
resultSize = sizeof(sSkillResult_Damage);
skillHandler = handleSkillDamage;
break;
case EST_HEAL_HP:
case EST_RETURNHOMEHEAL:
resultSize = sizeof(sSkillResult_Heal_HP);
skillHandler = handleSkillHealHP;
break;
case EST_JUMP:
case EST_RUN:
case EST_FREEDOM:
@ -54,28 +150,54 @@ void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICom
resultSize = sizeof(sSkillResult_Buff);
skillHandler = handleSkillBuff;
break;
case EST_BATTERYDRAIN:
resultSize = sizeof(sSkillResult_BatteryDrain);
skillHandler = handleSkillBatteryDrain;
break;
case EST_RECALL:
case EST_RECALL_GROUP:
resultSize = sizeof(sSkillResult_Move);
skillHandler = handleSkillMove;
break;
case EST_PHOENIX_GROUP:
resultSize = sizeof(sSkillResult_Resurrect);
skillHandler = handleSkillResurrect;
break;
default:
std::cout << "[WARN] Unhandled skill type " << skill->skillType << std::endl;
return;
return results;
}
assert(skillHandler != nullptr);
std::vector<SkillResult> results;
for(ICombatant* target : affected) {
SkillResult result = handleSkillBuff(skill, target);
for(ICombatant* target : targets) {
SkillResult result = skillHandler(skill, power, src, target);
if(result.size == 0) continue; // skill not applicable
if(result.size != resultSize) {
std::cout << "[WARN] bad skill result size for " << skill->skillType << " from " << handleSkillBuff << std::endl;
return;
std::cout << "[WARN] bad skill result size for " << skill->skillType << " from " << (void*)handleSkillBuff << std::endl;
continue;
}
results.push_back(result);
}
return results;
}
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), count, resultSize)) {
void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICombatant*> affected) {
if(SkillTable.count(nano.iSkillID) == 0)
return;
SkillData* skill = &SkillTable[nano.iSkillID];
Player* plr = PlayerManager::getPlayer(sock);
std::vector<SkillResult> results = handleSkill(skill, Nanos::getNanoBoost(plr), plr, affected);
size_t resultSize = results.back().size; // guaranteed to be the same for every item
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), results.size(), 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;
size_t resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + results.size() * resultSize;
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
memset(respbuf, 0, resplen);
@ -86,9 +208,9 @@ void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICom
pkt->iNanoStamina = nano.iStamina;
pkt->bNanoDeactive = nano.iStamina <= 0;
pkt->eST = skill->skillType;
pkt->iTargetCnt = count;
pkt->iTargetCnt = (int32_t)results.size();
char* appended = (char*)(pkt + 1);
uint8_t* appended = (uint8_t*)(pkt + 1);
for(SkillResult& result : results) {
memcpy(appended, result.payload, resultSize);
appended += resultSize;

View File

@ -32,11 +32,14 @@ enum class SkillDrainType {
struct SkillResult {
size_t size;
char payload[MAX_SKILLRESULT_SIZE];
uint8_t payload[MAX_SKILLRESULT_SIZE];
SkillResult(size_t len, void* dat) {
size = len;
memcpy(payload, dat, len);
}
SkillResult() {
size = 0;
}
};
struct SkillData {

View File

@ -63,12 +63,19 @@ int Player::getCompositeCondition() {
}
int Player::takeDamage(EntityRef src, int amt) {
HP -= amt;
return amt;
int dmg = amt;
if(HP - dmg < 0) dmg = HP;
HP -= dmg;
return dmg;
}
void Player::heal(EntityRef src, int amt) {
// stubbed
int Player::heal(EntityRef src, int amt) {
int heal = amt;
if(HP + heal > getMaxHP()) heal = getMaxHP() - HP;
HP += heal;
return heal;
}
bool Player::isAlive() {
@ -79,6 +86,14 @@ int Player::getCurrentHP() {
return HP;
}
int Player::getMaxHP() {
return PC_MAXHEALTH(level);
}
int Player::getLevel() {
return level;
}
std::vector<EntityRef> Player::getGroupMembers() {
std::vector<EntityRef> members;
if(group != nullptr)
@ -96,6 +111,10 @@ int32_t Player::getID() {
return iID;
}
EntityRef Player::getRef() {
return EntityRef(PlayerManager::getSockFromID(iID));
}
void Player::step(time_t currTime) {
// no-op
}
@ -121,16 +140,21 @@ int CombatNPC::getCompositeCondition() { /* stubbed */
}
int CombatNPC::takeDamage(EntityRef src, int amt) {
int dmg = amt;
if(hp - dmg < 0) dmg = hp;
hp -= dmg;
hp -= amt;
if (hp <= 0)
transition(AIState::DEAD, src);
if(hp <= 0) transition(AIState::DEAD, src);
return amt;
return dmg;
}
void CombatNPC::heal(EntityRef src, int amt) {
// stubbed
int CombatNPC::heal(EntityRef src, int amt) {
int heal = amt;
if(hp + heal > getMaxHP()) heal = getMaxHP() - hp;
hp += heal;
return heal;
}
bool CombatNPC::isAlive() {
@ -141,6 +165,14 @@ int CombatNPC::getCurrentHP() {
return hp;
}
int CombatNPC::getMaxHP() {
return maxHealth;
}
int CombatNPC::getLevel() {
return level;
}
std::vector<EntityRef> CombatNPC::getGroupMembers() {
std::vector<EntityRef> members;
if(group != nullptr)
@ -160,6 +192,10 @@ int32_t CombatNPC::getID() {
return id;
}
EntityRef CombatNPC::getRef() {
return EntityRef(id);
}
void CombatNPC::step(time_t currTime) {
if(stateHandlers.find(state) != stateHandlers.end())

View File

@ -83,6 +83,10 @@ void Egg::enterIntoViewOf(CNSocket *sock) {
sock->sendPacket(pkt, P_FE2CL_SHINY_ENTER);
}
sNano* Player::getActiveNano() {
return &Nanos[activeNano];
}
sPCAppearanceData Player::getAppearanceData() {
sPCAppearanceData data = {};
data.iID = iID;

View File

@ -53,12 +53,15 @@ public:
virtual bool hasBuff(int) = 0;
virtual int getCompositeCondition() = 0;
virtual int takeDamage(EntityRef, int) = 0;
virtual void heal(EntityRef, int) = 0;
virtual int heal(EntityRef, int) = 0;
virtual bool isAlive() = 0;
virtual int getCurrentHP() = 0;
virtual int getMaxHP() = 0;
virtual int getLevel() = 0;
virtual std::vector<EntityRef> getGroupMembers() = 0;
virtual int32_t getCharType() = 0;
virtual int32_t getID() = 0;
virtual EntityRef getRef() = 0;
virtual void step(time_t currTime) = 0;
};
@ -124,12 +127,15 @@ struct CombatNPC : public BaseNPC, public ICombatant {
virtual bool hasBuff(int buffId) override;
virtual int getCompositeCondition() override;
virtual int takeDamage(EntityRef src, int amt) override;
virtual void heal(EntityRef src, int amt) override;
virtual int heal(EntityRef src, int amt) override;
virtual bool isAlive() override;
virtual int getCurrentHP() override;
virtual int getMaxHP() override;
virtual int getLevel() override;
virtual std::vector<EntityRef> getGroupMembers() override;
virtual int32_t getCharType() override;
virtual int32_t getID() override;
virtual EntityRef getRef() override;
virtual void step(time_t currTime) override;
virtual void transition(AIState newState, EntityRef src);

View File

@ -95,14 +95,18 @@ struct Player : public Entity, public ICombatant {
virtual bool hasBuff(int buffId) override;
virtual int getCompositeCondition() override;
virtual int takeDamage(EntityRef src, int amt) override;
virtual void heal(EntityRef src, int amt) override;
virtual int heal(EntityRef src, int amt) override;
virtual bool isAlive() override;
virtual int getCurrentHP() override;
virtual int getMaxHP() override;
virtual int getLevel() override;
virtual std::vector<EntityRef> getGroupMembers() override;
virtual int32_t getCharType() override;
virtual int32_t getID() override;
virtual EntityRef getRef() override;
virtual void step(time_t currTime) override;
sNano* getActiveNano();
sPCAppearanceData getAppearanceData();
};

View File

@ -93,7 +93,7 @@ inline constexpr bool isOutboundPacketID(uint32_t id) {
// overflow-safe validation of variable-length packets
// for outbound packets
inline constexpr bool validOutVarPacket(size_t base, int32_t npayloads, size_t plsize) {
inline constexpr bool validOutVarPacket(size_t base, size_t npayloads, size_t plsize) {
// check for multiplication overflow
if (npayloads > 0 && (CN_PACKET_BUFFER_SIZE - 8) / (size_t)npayloads < plsize)
return false;
@ -110,7 +110,7 @@ inline constexpr bool validOutVarPacket(size_t base, int32_t npayloads, size_t p
}
// for inbound packets
inline constexpr bool validInVarPacket(size_t base, int32_t npayloads, size_t plsize, size_t datasize) {
inline constexpr bool validInVarPacket(size_t base, size_t npayloads, size_t plsize, size_t datasize) {
// check for multiplication overflow
if (npayloads > 0 && (CN_PACKET_BUFFER_SIZE - 8) / (size_t)npayloads < plsize)
return false;