mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-16 19:20:05 +00:00
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:
parent
723e455b1d
commit
9977907842
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user