Port egg buffs over to new system

This commit is contained in:
gsemaj 2022-07-30 13:47:27 -07:00
parent cb94018e46
commit 0994aaff71
14 changed files with 157 additions and 159 deletions

View File

@ -40,7 +40,7 @@ static SkillResult handleSkillHealHP(SkillData* skill, int power, ICombatant* so
sSkillResult_Heal_HP result{};
result.eCT = target->getCharType();
result.iID = target->getID();
result.iHealHP = heal;
result.iHealHP = healed;
result.iHP = target->getCurrentHP();
return SkillResult(sizeof(sSkillResult_Heal_HP), &result);
}
@ -170,7 +170,8 @@ static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombat
assert(skillHandler != nullptr);
for(ICombatant* target : targets) {
SkillResult result = skillHandler(skill, power, src, target);
assert(target != nullptr);
SkillResult result = skillHandler(skill, power, src != nullptr ? src : target, target);
if(result.size == 0) continue; // skill not applicable
if(result.size != resultSize) {
std::cout << "[WARN] bad skill result size for " << skill->skillType << " from " << (void*)handleSkillBuff << std::endl;
@ -181,7 +182,14 @@ static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombat
return results;
}
void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICombatant*> affected) {
static void attachSkillResults(std::vector<SkillResult> results, size_t resultSize, uint8_t* pivot) {
for(SkillResult& result : results) {
memcpy(pivot, result.payload, resultSize);
pivot += resultSize;
}
}
void Abilities::useNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICombatant*> affected) {
if(SkillTable.count(nano.iSkillID) == 0)
return;
@ -210,16 +218,45 @@ void Abilities::broadcastNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICom
pkt->eST = skill->skillType;
pkt->iTargetCnt = (int32_t)results.size();
uint8_t* appended = (uint8_t*)(pkt + 1);
for(SkillResult& result : results) {
memcpy(appended, result.payload, resultSize);
appended += resultSize;
}
attachSkillResults(results, resultSize, (uint8_t*)(pkt + 1));
sock->sendPacket(pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
}
void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector<ICombatant*> affected) {
if(SkillTable.count(skillID) == 0)
return;
Entity* entity = npc.getEntity();
ICombatant* src = nullptr;
if(npc.kind == EntityKind::COMBAT_NPC || npc.kind == EntityKind::MOB)
src = dynamic_cast<ICombatant*>(entity);
SkillData* skill = &SkillTable[skillID];
std::vector<SkillResult> results = handleSkill(skill, 0, src, affected);
size_t resultSize = results.back().size; // guaranteed to be the same for every item
if (!validOutVarPacket(sizeof(sP_FE2CL_NPC_SKILL_HIT), results.size(), resultSize)) {
std::cout << "[WARN] bad sP_FE2CL_NPC_SKILL_HIT packet size\n";
return;
}
// initialize response struct
size_t resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + results.size() * resultSize;
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
memset(respbuf, 0, resplen);
sP_FE2CL_NPC_SKILL_HIT* pkt = (sP_FE2CL_NPC_SKILL_HIT*)respbuf;
pkt->iNPC_ID = npc.id;
pkt->iSkillID = skillID;
pkt->eST = skill->skillType;
pkt->iTargetCnt = (int32_t)results.size();
attachSkillResults(results, resultSize, (uint8_t*)(pkt + 1));
NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen);
}
std::vector<EntityRef> Abilities::matchTargets(SkillData* skill, int count, int32_t *ids) {
std::vector<EntityRef> targets;
@ -303,53 +340,3 @@ int Abilities::getCSTBFromST(int eSkillType) {
}
return result;
}
std::vector<ICombatant*> Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr) {
assert(skill->drainType == SkillDrainType::PASSIVE);
EntityRef self = PlayerManager::getSockFromID(plr->iID);
std::vector<ICombatant*> affected;
std::vector<EntityRef> targets;
if (skill->targetType == SkillTargetType::GROUP) {
targets = plr->getGroupMembers(); // group
}
else if(skill->targetType == SkillTargetType::SELF) {
targets.push_back(self); // self
} else {
std::cout << "[WARN] Passive skill with type " << skill->skillType << " has target type MOB" << std::endl;
}
int timeBuffId = getCSTBFromST(skill->skillType);
int boost = Nanos::getNanoBoost(plr) ? 3 : 0;
int value = skill->values[0][boost];
BuffStack passiveBuff = {
1, // passive nano buffs refreshed every tick
value,
self,
BuffClass::NONE, // overwritten per target
};
for (EntityRef target : targets) {
Entity* entity = target.getEntity();
if (entity->kind != PLAYER && entity->kind != COMBAT_NPC && entity->kind != MOB)
continue; // not a combatant
passiveBuff.buffStackClass = target == self ? BuffClass::NANO : BuffClass::GROUP_NANO;
ICombatant* combatant = dynamic_cast<ICombatant*>(entity);
if(combatant->addBuff(timeBuffId,
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
Buffs::timeBuffUpdate(self, buff, status, stack);
},
[](EntityRef self, Buff* buff, time_t currTime) {
// TODO time buff tick
},
&passiveBuff)) affected.push_back(combatant);
}
return affected;
}
void Abilities::init() {
//REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_EMAIL_UPDATE_CHECK, emailUpdateCheck);
}

View File

@ -60,11 +60,9 @@ struct SkillData {
namespace Abilities {
extern std::map<int32_t, SkillData> SkillTable;
void broadcastNanoSkill(CNSocket*, sNano&, std::vector<ICombatant*>);
void useNanoSkill(CNSocket*, sNano&, std::vector<ICombatant*>);
void useNPCSkill(EntityRef, int skillID, std::vector<ICombatant*>);
std::vector<EntityRef> matchTargets(SkillData*, int, int32_t*);
int getCSTBFromST(int eSkillType);
std::vector<ICombatant*> usePassiveNanoSkill(SkillData*, Player*);
void init();
}

View File

@ -1,6 +1,7 @@
#include "Buffs.hpp"
#include "PlayerManager.hpp"
#include "NPCManager.hpp"
using namespace Buffs;
@ -120,19 +121,15 @@ void Buffs::timeBuffUpdate(EntityRef self, Buff* buff, int status, BuffStack* st
self.sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
}
/*
void Buffs::timeBuffTimeoutViewable(EntityRef self, Buff* buff, int ct) {
void Buffs::timeBuffTimeout(EntityRef self) {
if(self.kind != EntityKind::PLAYER && self.kind != EntityKind::COMBAT_NPC && self.kind != EntityKind::MOB)
return; // not a combatant
Entity* entity = self.getEntity();
ICombatant* combatant = dynamic_cast<ICombatant*>(entity);
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt); // send a buff timeout to other players
pkt.eCT = ct; // 1 for eggs, at least
pkt.iID = plr->iID;
pkt.iConditionBitFlag = plr->getCompositeCondition();
PlayerManager::sendToViewable(self.sock, pkt, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT);
pkt.eCT = combatant->getCharType();
pkt.iID = combatant->getID();
pkt.iConditionBitFlag = combatant->getCompositeCondition();
NPCManager::sendToViewable(entity, &pkt, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
}
*/
/* MOVE TO EGG LAMBDA
void Buffs::timeBuffTimeout(EntityRef self, BuffStack* buff) {
timeBuffUpdate(self, buff, ETBU_DEL);
timeBuffTimeoutViewable(self);
} */
#pragma endregion

View File

@ -84,5 +84,5 @@ public:
namespace Buffs {
void timeBuffUpdate(EntityRef self, Buff* buff, int status, BuffStack* stack);
//void timeBuffTimeoutViewable(EntityRef self, Buff* buff, int ct);
void timeBuffTimeout(EntityRef self);
}

View File

@ -805,7 +805,7 @@ static void playerTick(CNServer *serv, time_t currTime) {
if (skill->drainType == SkillDrainType::PASSIVE) {
// apply passive buff
drainRate = skill->batteryUse[boost * 3];
Abilities::usePassiveNanoSkill(skill, plr);
Nanos::applyNanoBuff(skill, plr);
}
}

View File

@ -11,6 +11,7 @@
#include "Missions.hpp"
#include "Eggs.hpp"
#include "Items.hpp"
#include "Abilities.hpp"
#include <sstream>
#include <limits.h>
@ -501,9 +502,12 @@ static void buffCommand(std::string full, std::vector<std::string>& args, CNSock
if (*tmp)
return;
if (Eggs::eggBuffPlayer(sock, skillId, 0, duration)<0)
if (Abilities::SkillTable.count(skillId) == 0) {
Chat::sendServerMessage(sock, "/buff: unknown skill Id");
return;
}
Eggs::eggBuffPlayer(sock, skillId, 0, duration);
}
static void eggCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {

View File

@ -15,63 +15,47 @@ using namespace Eggs;
std::unordered_map<int, EggType> Eggs::EggTypes;
int Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) {
void Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) {
Player* plr = PlayerManager::getPlayer(sock);
// TODO ABILITIES
// eggId might be 0 if the buff is made by the /buff command
EntityRef src = eggId == 0 ? sock : EntityRef(eggId);
size_t resplen;
if (skillId == 183) {
resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Damage);
} else if (skillId == 150) {
resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Heal_HP);
} else {
resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Buff);
}
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
// we know it's only one trailing struct, so we can skip full validation
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
auto skillUse = (sP_FE2CL_NPC_SKILL_HIT*)respbuf;
if (skillId == 183) { // damage egg
auto skill = (sSkillResult_Damage*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT));
memset(respbuf, 0, resplen);
skill->eCT = 1;
skill->iID = plr->iID;
skill->iDamage = PC_MAXHEALTH(plr->level) * Abilities::SkillTable[skillId].values[0][0] / 1000;
plr->HP -= skill->iDamage;
if (plr->HP < 0)
plr->HP = 0;
skill->iHP = plr->HP;
} else if (skillId == 150) { // heal egg
auto skill = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT));
memset(respbuf, 0, resplen);
skill->eCT = 1;
skill->iID = plr->iID;
skill->iHealHP = PC_MAXHEALTH(plr->level) * Abilities::SkillTable[skillId].values[0][0] / 1000;
plr->HP += skill->iHealHP;
if (plr->HP > PC_MAXHEALTH(plr->level))
plr->HP = PC_MAXHEALTH(plr->level);
skill->iHP = plr->HP;
} else { // regular buff egg
auto skill = (sSkillResult_Buff*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT));
memset(respbuf, 0, resplen);
skill->eCT = 1;
skill->iID = plr->iID;
skill->iConditionBitFlag = plr->getCompositeCondition();
if(Abilities::SkillTable.count(skillId) == 0) {
std::cout << "[WARN] egg " << eggId << " has skill ID " << skillId << " which doesn't exist" << std::endl;
return;
}
skillUse->iNPC_ID = eggId;
skillUse->iSkillID = skillId;
skillUse->eST = Abilities::SkillTable[skillId].skillType;
skillUse->iTargetCnt = 1;
SkillData* skill = &Abilities::SkillTable[skillId];
if(skill->drainType == SkillDrainType::PASSIVE) {
// apply buff
if(skill->targetType != SkillTargetType::SELF) {
std::cout << "[WARN] weird skill type for egg " << eggId << " with skill " << skillId << ", should be " << (int)skill->targetType << std::endl;
}
sock->sendPacket((void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen);
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_NPC_SKILL_HIT, resplen);
int timeBuffId = Abilities::getCSTBFromST(skill->skillType);
int value = skill->values[0][0];
BuffStack eggBuff = {
duration * 1000 / MS_PER_PLAYER_TICK,
value,
src,
BuffClass::EGG
};
plr->addBuff(timeBuffId,
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
Buffs::timeBuffUpdate(self, buff, status, stack);
if(status == ETBU_DEL) Buffs::timeBuffTimeout(self);
},
[](EntityRef self, Buff* buff, time_t currTime) {
// no-op
},
&eggBuff);
}
return 0;
// use skill
std::vector<ICombatant*> targets;
targets.push_back(dynamic_cast<ICombatant*>(plr));
Abilities::useNPCSkill(src, skillId, targets);
}
static void eggStep(CNServer* serv, time_t currTime) {
@ -141,16 +125,13 @@ static void eggPickup(CNSocket* sock, CNPacketData* data) {
EggType* type = &EggTypes[typeId];
// buff the player
if (type->effectId != 0)
eggBuffPlayer(sock, type->effectId, eggRef.id, type->duration);
/*
* SHINY_PICKUP_SUCC is only causing a GUI effect in the client
* (buff icon pops up in the bottom of the screen)
* so we don't send it for non-effect
*/
if (type->effectId != 0) {
eggBuffPlayer(sock, type->effectId, eggRef.id, type->duration);
INITSTRUCT(sP_FE2CL_REP_SHINY_PICKUP_SUCC, resp);
resp.iSkillID = type->effectId;

View File

@ -14,7 +14,6 @@ namespace Eggs {
void init();
/// returns -1 on fail
int eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration);
void eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration);
void npcDataToEggData(int x, int y, int z, sNPCAppearanceData* npc, sShinyAppearanceData* egg);
}

View File

@ -240,27 +240,11 @@ void Groups::groupTickInfo(Player* plr) {
sendToGroup(plr->group, (void*)&respbuf, P_FE2CL_PC_GROUP_MEMBER_INFO, resplen);
}
static void groupUnbuff(Player* plr) {
Group* group = plr->group;
for (int i = 0; i < group->members.size(); i++) {
for (int n = 0; n < group->members.size(); n++) {
if (i == n)
continue;
EntityRef other = group->members[n];
// TODO ABILITIES
//Abilities::applyBuff(sock, otherPlr->Nanos[otherPlr->activeNano].iSkillID, 2, 1, 0);
}
}
}
void Groups::groupKick(Player* plr) {
Group* group = plr->group;
// if you are the group leader, destroy your own group and kick everybody
if (plr->group->members[0] == PlayerManager::getSockFromID(plr->iID)) {
groupUnbuff(plr);
INITSTRUCT(sP_FE2CL_PC_GROUP_LEAVE_SUCC, resp1);
sendToGroup(plr->group, (void*)&resp1, P_FE2CL_PC_GROUP_LEAVE_SUCC, sizeof(sP_FE2CL_PC_GROUP_LEAVE_SUCC));
disbandGroup(plr->group);

View File

@ -503,7 +503,7 @@ static void itemUseHandler(CNSocket* sock, CNPacketData* data) {
Buffs::timeBuffUpdate(self, buff, status, stack);
},
[](EntityRef self, Buff* buff, time_t currTime) {
// TODO passive tick
// no-op
},
&gumballBuff);

View File

@ -81,7 +81,7 @@ void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z, uint64_t I,
Chunking::updateEntityChunk({id}, oldChunk, newChunk);
}
void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t size) {
void NPCManager::sendToViewable(Entity *npc, void *buf, uint32_t type, size_t size) {
for (auto it = npc->viewableChunks.begin(); it != npc->viewableChunks.end(); it++) {
Chunk* chunk = *it;
for (const EntityRef& ref : chunk->entities) {

View File

@ -42,7 +42,7 @@ namespace NPCManager {
void destroyNPC(int32_t);
void updateNPCPosition(int32_t, int X, int Y, int Z, uint64_t I, int angle);
void sendToViewable(BaseNPC* npc, void* buf, uint32_t type, size_t size);
void sendToViewable(Entity* npc, void* buf, uint32_t type, size_t size);
BaseNPC *summonNPC(int x, int y, int z, uint64_t instance, int type, bool respawn=false, bool baseInstance=false);

View File

@ -69,6 +69,52 @@ void Nanos::addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm)
PlayerManager::sendToViewable(sock, resp2, P_FE2CL_REP_PC_CHANGE_LEVEL);
}
std::vector<ICombatant*> Nanos::applyNanoBuff(SkillData* skill, Player* plr) {
assert(skill->drainType == SkillDrainType::PASSIVE);
EntityRef self = PlayerManager::getSockFromID(plr->iID);
std::vector<ICombatant*> affected;
std::vector<EntityRef> targets;
if (skill->targetType == SkillTargetType::GROUP) {
targets = plr->getGroupMembers(); // group
}
else if(skill->targetType == SkillTargetType::SELF) {
targets.push_back(self); // self
} else {
std::cout << "[WARN] Passive skill with type " << skill->skillType << " has target type MOB" << std::endl;
}
int timeBuffId = Abilities::getCSTBFromST(skill->skillType);
int boost = Nanos::getNanoBoost(plr) ? 3 : 0;
int value = skill->values[0][boost];
BuffStack passiveBuff = {
1, // passive nano buffs refreshed every tick
value,
self,
BuffClass::NONE, // overwritten per target
};
for (EntityRef target : targets) {
Entity* entity = target.getEntity();
if (entity->kind != PLAYER && entity->kind != COMBAT_NPC && entity->kind != MOB)
continue; // not a combatant
passiveBuff.buffStackClass = target == self ? BuffClass::NANO : BuffClass::GROUP_NANO;
ICombatant* combatant = dynamic_cast<ICombatant*>(entity);
if(combatant->addBuff(timeBuffId,
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
Buffs::timeBuffUpdate(self, buff, status, stack);
},
[](EntityRef self, Buff* buff, time_t currTime) {
// no-op
},
&passiveBuff)) affected.push_back(combatant);
}
return affected;
}
void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
INITSTRUCT(sP_FE2CL_REP_NANO_ACTIVE_SUCC, resp);
resp.iActiveNanoSlotNum = slot;
@ -94,8 +140,8 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
// passive buff effect
resp.eCSTB___Add = 1;
int boost = Nanos::getNanoBoost(plr);
std::vector<ICombatant*> affectedCombatants = Abilities::usePassiveNanoSkill(skill, plr);
if(!affectedCombatants.empty()) Abilities::broadcastNanoSkill(sock, nano, affectedCombatants);
std::vector<ICombatant*> affectedCombatants = applyNanoBuff(skill, plr);
if(!affectedCombatants.empty()) Abilities::useNanoSkill(sock, nano, affectedCombatants);
}
if (!silent) // silent nano death but only for the summoning player

View File

@ -3,6 +3,7 @@
#include "core/Core.hpp"
#include "Player.hpp"
#include "Abilities.hpp"
#include <map>
@ -25,4 +26,5 @@ namespace Nanos {
void summonNano(CNSocket* sock, int slot, bool silent = false);
int nanoStyle(int nanoID);
bool getNanoBoost(Player* plr);
std::vector<ICombatant*> applyNanoBuff(SkillData* skill, Player* plr);
}