YET ANOTHER ITERATION of the new ability system

I am very tired
This commit is contained in:
gsemaj 2022-07-22 06:47:52 -07:00
parent 5b58055924
commit d4253c3b49
10 changed files with 214 additions and 169 deletions

View File

@ -104,9 +104,11 @@ int Abilities::getCSTBFromST(int eSkillType) {
} }
#pragma region Skill Handlers #pragma region Skill Handlers
void Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) { bool Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) {
assert(skill->drainType == SkillDrainType::PASSIVE); assert(skill->drainType == SkillDrainType::PASSIVE);
bool newBuffApplied = false;
EntityRef self = PlayerManager::getSockFromID(plr->iID); EntityRef self = PlayerManager::getSockFromID(plr->iID);
std::vector<EntityRef> targets; std::vector<EntityRef> targets;
if (skill->targetType == SkillTargetType::GROUP) { if (skill->targetType == SkillTargetType::GROUP) {
@ -126,14 +128,6 @@ void Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) {
value, value,
self, self,
BuffClass::NONE, // overwritten per target BuffClass::NONE, // overwritten per target
[](EntityRef host, BuffStack* stack) {
Buffs::timeBuffUpdate(host, stack, ETBU_ADD);
// TODO SKILLUSE/SKILLUSESUCC, sSkillResult_Buff
},
nullptr,
[](EntityRef host, BuffStack* stack) {
Buffs::timeBuffUpdate(host, stack, ETBU_DEL);
}
}; };
for (EntityRef target : targets) { for (EntityRef target : targets) {
@ -143,8 +137,17 @@ void Abilities::usePassiveNanoSkill(SkillData* skill, Player* plr, int boost) {
passiveBuff.buffStackClass = target == self ? BuffClass::NANO : BuffClass::GROUP_NANO; passiveBuff.buffStackClass = target == self ? BuffClass::NANO : BuffClass::GROUP_NANO;
ICombatant* combatant = dynamic_cast<ICombatant*>(entity); ICombatant* combatant = dynamic_cast<ICombatant*>(entity);
combatant->addBuff(timeBuffId, &passiveBuff); newBuffApplied |= 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);
} }
return newBuffApplied;
} }
#pragma endregion #pragma endregion

View File

@ -47,7 +47,7 @@ namespace Abilities {
std::vector<EntityRef> matchTargets(SkillData*, int, int32_t*); std::vector<EntityRef> matchTargets(SkillData*, int, int32_t*);
int getCSTBFromST(int eSkillType); int getCSTBFromST(int eSkillType);
void usePassiveNanoSkill(SkillData*, Player*, int); bool usePassiveNanoSkill(SkillData*, Player*, int);
void init(); void init();
} }

View File

@ -4,19 +4,16 @@
using namespace Buffs; using namespace Buffs;
void Buff::tick() { void Buff::tick(time_t currTime) {
auto it = stacks.begin(); auto it = stacks.begin();
while(it != stacks.end()) { while(it != stacks.end()) {
BuffStack& stack = *it; BuffStack& stack = *it;
if(stack.onTick) stack.onTick(self, &stack); if(onTick) onTick(self, this, currTime);
if(stack.durationTicks == 0) { if(stack.durationTicks == 0) {
// erase() destroys the callbacks
// with the stack struct, so we need
// to copy it first.
BuffStack deadStack = stack; BuffStack deadStack = stack;
it = stacks.erase(it); it = stacks.erase(it);
if(deadStack.onExpire) deadStack.onExpire(self, &deadStack); if(onUpdate) onUpdate(self, this, ETBU_DEL, &deadStack);
} else { } else {
if(stack.durationTicks > 0) stack.durationTicks--; if(stack.durationTicks > 0) stack.durationTicks--;
it++; it++;
@ -28,15 +25,13 @@ void Buff::clear() {
while(!stacks.empty()) { while(!stacks.empty()) {
BuffStack stack = stacks.back(); BuffStack stack = stacks.back();
stacks.pop_back(); stacks.pop_back();
if(stack.onExpire) stack.onExpire(self, &stack); if(onUpdate) onUpdate(self, this, ETBU_DEL, &stack);
} }
} }
void Buff::addStack(BuffStack* stack) { void Buff::addStack(BuffStack* stack) {
BuffStack newStack = *stack; stacks.push_back(*stack);
newStack.buff = this; if(onUpdate) onUpdate(self, this, ETBU_ADD, &stacks.back());
if(newStack.onApply) newStack.onApply(self, &newStack);
stacks.push_back(newStack);
} }
bool Buff::hasClass(BuffClass buffClass) { bool Buff::hasClass(BuffClass buffClass) {
@ -56,34 +51,68 @@ BuffClass Buff::maxClass() {
return buffClass; return buffClass;
} }
int Buff::getValue(BuffValueSelector selector) {
if(isStale()) return 0;
int value = selector == BuffValueSelector::NET_TOTAL ? 0 : stacks.front().value;
for(BuffStack& stack : stacks) {
switch(selector)
{
case BuffValueSelector::NET_TOTAL:
value += stack.value;
break;
case BuffValueSelector::MIN_VALUE:
if(stack.value < value) value = stack.value;
break;
case BuffValueSelector::MAX_VALUE:
if(stack.value > value) value = stack.value;
break;
case BuffValueSelector::MIN_MAGNITUDE:
if(abs(stack.value) < abs(value)) value = stack.value;
break;
case BuffValueSelector::MAX_MAGNITUDE:
default:
if(abs(stack.value) > abs(value)) value = stack.value;
}
}
return value;
}
bool Buff::isStale() { bool Buff::isStale() {
return stacks.empty(); return stacks.empty();
} }
/* This will practically never do anything important, but it's here just in case */
void Buff::updateCallbacks(BuffCallback<int, BuffStack*> fOnUpdate, BuffCallback<time_t> fonTick) {
if(!onUpdate) onUpdate = fOnUpdate;
if(!onTick) onTick = fonTick;
}
#pragma region Handlers #pragma region Handlers
void Buffs::timeBuffUpdate(EntityRef self, BuffStack* stack, int status) { void Buffs::timeBuffUpdate(EntityRef self, Buff* buff, int status, BuffStack* stack) {
if(self.kind != EntityKind::PLAYER) if(self.kind != EntityKind::PLAYER)
return; // not implemented return; // not implemented
Player* plr = (Player*)self.getEntity(); Player* plr = (Player*)self.getEntity();
if(plr == nullptr) if(plr == nullptr)
return; return; // sanity check
if(status == ETBU_DEL && plr->hasBuff(stack->buff->id)) if(status == ETBU_DEL && !buff->isStale())
return; // no premature status removal! return; // no premature effect deletion
sTimeBuff payload{};
int cbf = plr->getCompositeCondition(); int cbf = plr->getCompositeCondition();
sTimeBuff payload{};
if(status == ETBU_ADD) { if(status == ETBU_ADD) {
if(stack->buff->id > 0) cbf |= CSB_FROM_ECSB(stack->buff->id); payload.iValue = buff->getValue(BuffValueSelector::MAX_MAGNITUDE);
payload.iValue = stack->value; // we need to explicitly add the ECSB for this buff,
//payload.iTimeLimit = stack->durationTicks * MS_PER_PLAYER_TICK; // in case this is the first stack in and the entry
// in the buff map doesn't yet exist
if(buff->id > 0) cbf |= CSB_FROM_ECSB(buff->id);
} }
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt); INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt);
pkt.eCSTB = stack->buff->id; // eCharStatusTimeBuffID pkt.eCSTB = buff->id; // eCharStatusTimeBuffID
pkt.eTBU = status; // eTimeBuffUpdate pkt.eTBU = status; // eTimeBuffUpdate
pkt.eTBT = (int)stack->buffStackClass; pkt.eTBT = (int)stack->buffStackClass;
pkt.iConditionBitFlag = cbf; pkt.iConditionBitFlag = cbf;
@ -91,20 +120,15 @@ void Buffs::timeBuffUpdate(EntityRef self, BuffStack* stack, int status) {
self.sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); self.sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
} }
void Buffs::timeBuffTimeoutViewable(EntityRef self, BuffStack* stack, int ct) { /*
if(self.kind != EntityKind::PLAYER) void Buffs::timeBuffTimeoutViewable(EntityRef self, Buff* buff, int ct) {
return; // not implemented
Player* plr = (Player*)self.getEntity();
if(plr == nullptr)
return;
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt); // send a buff timeout to other players 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.eCT = ct; // 1 for eggs, at least
pkt.iID = plr->iID; pkt.iID = plr->iID;
pkt.iConditionBitFlag = plr->getCompositeCondition(); pkt.iConditionBitFlag = plr->getCompositeCondition();
PlayerManager::sendToViewable(self.sock, pkt, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT); PlayerManager::sendToViewable(self.sock, pkt, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT);
} }
*/
/* MOVE TO EGG LAMBDA /* MOVE TO EGG LAMBDA
void Buffs::timeBuffTimeout(EntityRef self, BuffStack* buff) { void Buffs::timeBuffTimeout(EntityRef self, BuffStack* buff) {

View File

@ -2,13 +2,15 @@
#include "core/Core.hpp" #include "core/Core.hpp"
#include "Entities.hpp" #include "EntityRef.hpp"
#include <vector> #include <vector>
#include <functional> #include <functional>
/* forward declaration(s) */ /* forward declaration(s) */
struct BuffStack; class Buff;
template<class... Types>
using BuffCallback = std::function<void(EntityRef, Buff*, Types...)>;
#define CSB_FROM_ECSB(x) (1 << (x - 1)) #define CSB_FROM_ECSB(x) (1 << (x - 1))
@ -22,24 +24,19 @@ enum class BuffClass {
CASH_ITEM = ETBT_CASHITEM CASH_ITEM = ETBT_CASHITEM
}; };
typedef std::function<void(EntityRef, BuffStack*)> BuffCallback; enum class BuffValueSelector {
MAX_VALUE,
MIN_VALUE,
MAX_MAGNITUDE,
MIN_MAGNITUDE,
NET_TOTAL
};
struct BuffStack { struct BuffStack {
int durationTicks; int durationTicks;
int value; int value;
EntityRef source; EntityRef source;
BuffClass buffStackClass; BuffClass buffStackClass;
/* called just before the stack is added */
BuffCallback onApply;
/* called when the stack is ticked */
BuffCallback onTick;
/* called just after the stack is removed */
BuffCallback onExpire;
Buff* buff;
}; };
class Buff { class Buff {
@ -49,8 +46,12 @@ private:
public: public:
int id; int id;
/* called just after a stack is added or removed */
BuffCallback<int, BuffStack*> onUpdate;
/* called when the buff is ticked */
BuffCallback<time_t> onTick;
void tick(); void tick(time_t);
void clear(); void clear();
void addStack(BuffStack* stack); void addStack(BuffStack* stack);
@ -62,6 +63,8 @@ public:
bool hasClass(BuffClass buffClass); bool hasClass(BuffClass buffClass);
BuffClass maxClass(); BuffClass maxClass();
int getValue(BuffValueSelector selector);
/* /*
* In general, a Buff object won't exist * In general, a Buff object won't exist
* unless it has stacks. However, when * unless it has stacks. However, when
@ -71,13 +74,15 @@ public:
*/ */
bool isStale(); bool isStale();
Buff(int iid, EntityRef pSelf, BuffStack* firstStack) void updateCallbacks(BuffCallback<int, BuffStack*> fOnUpdate, BuffCallback<time_t> fonTick);
: id(iid), self(pSelf) {
Buff(int iid, EntityRef pSelf, BuffCallback<int, BuffStack*> fOnUpdate, BuffCallback<time_t> fOnTick, BuffStack* firstStack)
: self(pSelf), id(iid), onUpdate(fOnUpdate), onTick(fOnTick) {
addStack(firstStack); addStack(firstStack);
} }
}; };
namespace Buffs { namespace Buffs {
void timeBuffUpdate(EntityRef self, BuffStack* stack, int status); void timeBuffUpdate(EntityRef self, Buff* buff, int status, BuffStack* stack);
void timeBuffTimeoutViewable(EntityRef self, BuffStack* stack, int ct); //void timeBuffTimeoutViewable(EntityRef self, Buff* buff, int ct);
} }

View File

@ -19,10 +19,17 @@ 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::addBuff(int buffId, BuffStack* stack) { bool Player::addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) {
EntityRef self = PlayerManager::getSockFromID(iID); EntityRef self = PlayerManager::getSockFromID(iID);
if(!hasBuff(buffId)) buffs[buffId] = new Buff(buffId, self, stack);
else buffs[buffId]->addStack(stack); if(!hasBuff(buffId)) {
buffs[buffId] = new Buff(buffId, self, onUpdate, onTick, stack);
return true;
}
buffs[buffId]->updateCallbacks(onUpdate, onTick);
buffs[buffId]->addStack(stack);
return false;
} }
Buff* Player::getBuff(int buffId) { Buff* Player::getBuff(int buffId) {
@ -88,7 +95,9 @@ void Player::step(time_t currTime) {
// no-op // no-op
} }
void CombatNPC::addBuff(int buffId, BuffStack* stack) { /* stubbed */ } bool CombatNPC::addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) { /* stubbed */
return false;
}
Buff* CombatNPC::getBuff(int buffId) { /* stubbed */ Buff* CombatNPC::getBuff(int buffId) { /* stubbed */
return nullptr; return nullptr;
@ -342,43 +351,20 @@ static void combatEnd(CNSocket *sock, CNPacketData *data) {
plr->healCooldown = 4000; plr->healCooldown = 4000;
} }
static void dotDamageOnOff(CNSocket *sock, CNPacketData *data) { static void dealGooDamage(CNSocket *sock) {
sP_CL2FE_DOT_DAMAGE_ONOFF *pkt = (sP_CL2FE_DOT_DAMAGE_ONOFF*)data->buf;
Player *plr = PlayerManager::getPlayer(sock); Player *plr = PlayerManager::getPlayer(sock);
if(plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE)
return; // ignore completely
// infection debuff toggles as the client asks it to,
// so we add and remove a permanent debuff
if (pkt->iFlag && !plr->hasBuff(ECSB_INFECTION)) {
BuffStack infection = {
-1, // infinite
NULL,
sock, // self-inflicted
BuffClass::ENVIRONMENT,
[](EntityRef host, BuffStack* stack) {
Buffs::timeBuffUpdate(host, stack, ETBU_ADD);
},
nullptr, // client toggles for us! todo anticheat lol
[](EntityRef host, BuffStack* stack) {
Buffs::timeBuffUpdate(host, stack, ETBU_DEL);
}
};
plr->addBuff(ECSB_INFECTION, &infection);
} else if(!pkt->iFlag && plr->hasBuff(ECSB_INFECTION)) {
plr->removeBuff(ECSB_INFECTION);
}
}
static void dealGooDamage(CNSocket *sock, int amount) {
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_DotDamage); size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_DotDamage);
assert(resplen < CN_PACKET_BUFFER_SIZE - 8); assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
uint8_t respbuf[CN_PACKET_BUFFER_SIZE]; uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
Player *plr = PlayerManager::getPlayer(sock);
memset(respbuf, 0, resplen); memset(respbuf, 0, resplen);
sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf; sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf;
sSkillResult_DotDamage *dmg = (sSkillResult_DotDamage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK)); sSkillResult_DotDamage *dmg = (sSkillResult_DotDamage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK));
int amount = PC_MAXHEALTH(plr->level) * 3 / 20;
Buff* protectionBuff = plr->getBuff(ECSB_PROTECT_INFECTION); Buff* protectionBuff = plr->getBuff(ECSB_PROTECT_INFECTION);
if (protectionBuff != nullptr) { if (protectionBuff != nullptr) {
amount = -2; // -2 is the magic number for "Protected" to appear as the damage number amount = -2; // -2 is the magic number for "Protected" to appear as the damage number
@ -415,6 +401,33 @@ static void dealGooDamage(CNSocket *sock, int amount) {
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
} }
static void dotDamageOnOff(CNSocket *sock, CNPacketData *data) {
sP_CL2FE_DOT_DAMAGE_ONOFF *pkt = (sP_CL2FE_DOT_DAMAGE_ONOFF*)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// infection debuff toggles as the client asks it to,
// so we add and remove a permanent debuff
if (pkt->iFlag && !plr->hasBuff(ECSB_INFECTION)) {
BuffStack infection = {
-1, // infinite
0, // no value
sock, // self-inflicted
BuffClass::ENVIRONMENT
};
plr->addBuff(ECSB_INFECTION,
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
Buffs::timeBuffUpdate(self, buff, status, stack);
},
[](EntityRef self, Buff* buff, time_t currTime) {
if(self.kind == EntityKind::PLAYER)
dealGooDamage(self.sock);
},
&infection);
} else if(!pkt->iFlag && plr->hasBuff(ECSB_INFECTION)) {
plr->removeBuff(ECSB_INFECTION);
}
}
static void pcAttackChars(CNSocket *sock, CNPacketData *data) { static void pcAttackChars(CNSocket *sock, CNPacketData *data) {
sP_CL2FE_REQ_PC_ATTACK_CHARs* pkt = (sP_CL2FE_REQ_PC_ATTACK_CHARs*)data->buf; sP_CL2FE_REQ_PC_ATTACK_CHARs* pkt = (sP_CL2FE_REQ_PC_ATTACK_CHARs*)data->buf;
Player *plr = PlayerManager::getPlayer(sock); Player *plr = PlayerManager::getPlayer(sock);
@ -716,11 +729,6 @@ static void playerTick(CNServer *serv, time_t currTime) {
if (plr->HP <= 0) if (plr->HP <= 0)
continue; continue;
// fm patch/lake damage
if ((plr->hasBuff(ECSB_INFECTION))
&& !(plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE))
dealGooDamage(sock, PC_MAXHEALTH(plr->level) * 3 / 20);
// heal // heal
if (currTime - lastHealTime >= 4000 && !plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) { if (currTime - lastHealTime >= 4000 && !plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) {
if (currTime - lastHealTime - plr->healCooldown >= 4000) { if (currTime - lastHealTime - plr->healCooldown >= 4000) {
@ -747,7 +755,10 @@ static void playerTick(CNServer *serv, time_t currTime) {
if (skill->drainType == SkillDrainType::PASSIVE) { if (skill->drainType == SkillDrainType::PASSIVE) {
// apply passive buff // apply passive buff
drainRate = skill->batteryUse[boost * 3]; drainRate = skill->batteryUse[boost * 3];
Abilities::usePassiveNanoSkill(skill, plr, boost * 3); if(Abilities::usePassiveNanoSkill(skill, plr, boost * 3)) {
// first buff, send back skill use packet(s)
// TODO
}
} }
} }
@ -783,9 +794,8 @@ static void playerTick(CNServer *serv, time_t currTime) {
// process buffsets // process buffsets
auto it = plr->buffs.begin(); auto it = plr->buffs.begin();
while(it != plr->buffs.end()) { while(it != plr->buffs.end()) {
int buffId = (*it).first;
Buff* buff = (*it).second; Buff* buff = (*it).second;
buff->tick(); buff->tick(currTime);
if(buff->isStale()) { if(buff->isStale()) {
// garbage collect // garbage collect
it = plr->buffs.erase(it); it = plr->buffs.erase(it);

View File

@ -3,22 +3,16 @@
#include "core/Core.hpp" #include "core/Core.hpp"
#include "Chunking.hpp" #include "Chunking.hpp"
#include "EntityRef.hpp"
#include "Buffs.hpp"
#include <set> #include <set>
#include <map> #include <map>
#include <functional>
/* forward declaration(s) */ /* forward declaration(s) */
class Buff; class Chunk;
struct BuffStack; struct Group;
enum EntityKind {
INVALID,
PLAYER,
SIMPLE_NPC,
COMBAT_NPC,
MOB,
EGG,
BUS
};
enum class AIState { enum class AIState {
INACTIVE, INACTIVE,
@ -28,9 +22,6 @@ enum class AIState {
DEAD DEAD
}; };
class Chunk;
struct Group;
struct Entity { struct Entity {
EntityKind kind = EntityKind::INVALID; EntityKind kind = EntityKind::INVALID;
int x = 0, y = 0, z = 0; int x = 0, y = 0, z = 0;
@ -48,42 +39,6 @@ struct Entity {
virtual void disappearFromViewOf(CNSocket *sock) = 0; virtual void disappearFromViewOf(CNSocket *sock) = 0;
}; };
struct EntityRef {
EntityKind kind;
union {
CNSocket *sock;
int32_t id;
};
EntityRef(CNSocket *s);
EntityRef(int32_t i);
bool isValid() const;
Entity *getEntity() const;
bool operator==(const EntityRef& other) const {
if (kind != other.kind)
return false;
if (kind == EntityKind::PLAYER)
return sock == other.sock;
return id == other.id;
}
// arbitrary ordering
bool operator<(const EntityRef& other) const {
if (kind == other.kind) {
if (kind == EntityKind::PLAYER)
return sock < other.sock;
else
return id < other.id;
}
return kind < other.kind;
}
};
/* /*
* Interfaces * Interfaces
*/ */
@ -92,10 +47,10 @@ public:
ICombatant() {} ICombatant() {}
virtual ~ICombatant() {} virtual ~ICombatant() {}
virtual void addBuff(int buffId, BuffStack* stack) = 0; virtual bool addBuff(int, BuffCallback<int, BuffStack*>, BuffCallback<time_t>, BuffStack*) = 0;
virtual Buff* getBuff(int buffId) = 0; virtual Buff* getBuff(int) = 0;
virtual void removeBuff(int buffId) = 0; virtual void removeBuff(int) = 0;
virtual bool hasBuff(int buffId) = 0; virtual bool hasBuff(int) = 0;
virtual int getCompositeCondition() = 0; virtual int getCompositeCondition() = 0;
virtual int takeDamage(EntityRef, int) = 0; virtual int takeDamage(EntityRef, int) = 0;
virtual void heal(EntityRef, int) = 0; virtual void heal(EntityRef, int) = 0;
@ -159,7 +114,7 @@ struct CombatNPC : public BaseNPC, public ICombatant {
virtual bool isExtant() override { return hp > 0; } virtual bool isExtant() override { return hp > 0; }
virtual void addBuff(int buffId, BuffStack* stack) override; virtual bool addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) override;
virtual Buff* getBuff(int buffId) override; virtual Buff* getBuff(int buffId) override;
virtual void removeBuff(int buffId) override; virtual void removeBuff(int buffId) override;
virtual bool hasBuff(int buffId) override; virtual bool hasBuff(int buffId) override;

52
src/EntityRef.hpp Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include "core/Core.hpp"
/* forward declaration(s) */
struct Entity;
enum EntityKind {
INVALID,
PLAYER,
SIMPLE_NPC,
COMBAT_NPC,
MOB,
EGG,
BUS
};
struct EntityRef {
EntityKind kind;
union {
CNSocket *sock;
int32_t id;
};
EntityRef(CNSocket *s);
EntityRef(int32_t i);
bool isValid() const;
Entity *getEntity() const;
bool operator==(const EntityRef& other) const {
if (kind != other.kind)
return false;
if (kind == EntityKind::PLAYER)
return sock == other.sock;
return id == other.id;
}
// arbitrary ordering
bool operator<(const EntityRef& other) const {
if (kind == other.kind) {
if (kind == EntityKind::PLAYER)
return sock < other.sock;
else
return id < other.id;
}
return kind < other.kind;
}
};

View File

@ -494,18 +494,18 @@ static void itemUseHandler(CNSocket* sock, CNPacketData* data) {
int durationMilliseconds = Abilities::SkillTable[144].durationTime[0] * 100; int durationMilliseconds = Abilities::SkillTable[144].durationTime[0] * 100;
BuffStack gumballBuff = { BuffStack gumballBuff = {
durationMilliseconds / MS_PER_PLAYER_TICK, durationMilliseconds / MS_PER_PLAYER_TICK,
NULL, 0,
sock, sock,
BuffClass::CASH_ITEM, // or BuffClass::ITEM? BuffClass::CASH_ITEM // or BuffClass::ITEM?
[](EntityRef host, BuffStack* stack) {
Buffs::timeBuffUpdate(host, stack, ETBU_ADD);
},
nullptr,
[](EntityRef host, BuffStack* stack) {
Buffs::timeBuffUpdate(host, stack, ETBU_DEL);
}
}; };
player->addBuff(eCSB, &gumballBuff); player->addBuff(eCSB,
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
Buffs::timeBuffUpdate(self, buff, status, stack);
},
[](EntityRef self, Buff* buff, time_t currTime) {
// TODO passive tick
},
&gumballBuff);
sock->sendPacket((void*)&respbuf, P_FE2CL_REP_PC_ITEM_USE_SUCC, resplen); sock->sendPacket((void*)&respbuf, P_FE2CL_REP_PC_ITEM_USE_SUCC, resplen);
// update inventory serverside // update inventory serverside

View File

@ -208,10 +208,6 @@ static void nanoEquipHandler(CNSocket* sock, CNPacketData* data) {
// Update player // Update player
plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID; plr->equippedNanos[nano->iNanoSlotNum] = nano->iNanoID;
// Unbuff gumballs
int buffId = ECSB_STIMPAKSLOT1 + nano->iNanoSlotNum;
plr->removeBuff(buffId);
// unsummon nano if replaced // unsummon nano if replaced
if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum]) if (plr->activeNano == plr->equippedNanos[nano->iNanoSlotNum])
summonNano(sock, -1); summonNano(sock, -1);

View File

@ -87,7 +87,7 @@ 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 addBuff(int buffId, BuffStack* stack) override; virtual bool addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) override;
virtual Buff* getBuff(int buffId) override; virtual Buff* getBuff(int buffId) override;
virtual void removeBuff(int buffId) override; virtual void removeBuff(int buffId) override;
virtual bool hasBuff(int buffId) override; virtual bool hasBuff(int buffId) override;