mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-04 14:30:05 +00:00
[refactor] Active power handling
This commit is contained in:
parent
4ece1bb89b
commit
82bee2051a
@ -5,8 +5,6 @@
|
|||||||
#include "Buffs.hpp"
|
#include "Buffs.hpp"
|
||||||
#include "Nanos.hpp"
|
#include "Nanos.hpp"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
using namespace Abilities;
|
using namespace Abilities;
|
||||||
|
|
||||||
std::map<int32_t, SkillData> Abilities::SkillTable;
|
std::map<int32_t, SkillData> Abilities::SkillTable;
|
||||||
@ -35,7 +33,8 @@ static SkillResult handleSkillDamage(SkillData* skill, int power, ICombatant* so
|
|||||||
static SkillResult handleSkillHealHP(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
static SkillResult handleSkillHealHP(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
||||||
EntityRef sourceRef = source->getRef();
|
EntityRef sourceRef = source->getRef();
|
||||||
int heal = skill->values[0][power];
|
int heal = skill->values[0][power];
|
||||||
int healed = target->heal(sourceRef, heal);
|
double scalingFactor = target->getMaxHP() / 1000.0;
|
||||||
|
int healed = target->heal(sourceRef, heal * scalingFactor);
|
||||||
|
|
||||||
sSkillResult_Heal_HP result{};
|
sSkillResult_Heal_HP result{};
|
||||||
result.eCT = target->getCharType();
|
result.eCT = target->getCharType();
|
||||||
@ -46,16 +45,67 @@ static SkillResult handleSkillHealHP(SkillData* skill, int power, ICombatant* so
|
|||||||
}
|
}
|
||||||
|
|
||||||
static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
static SkillResult handleSkillDamageNDebuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
||||||
// TODO abilities
|
// take aggro
|
||||||
|
target->takeDamage(source->getRef(), 0);
|
||||||
|
|
||||||
|
int duration = 0;
|
||||||
|
int strength = 0;
|
||||||
|
bool blocked = target->hasBuff(ECSB_FREEDOM);
|
||||||
|
if(!blocked) {
|
||||||
|
duration = skill->durationTime[power];
|
||||||
|
strength = skill->values[0][power];
|
||||||
|
BuffStack debuff = {
|
||||||
|
duration, // ticks
|
||||||
|
strength, // value
|
||||||
|
source->getRef(), // source
|
||||||
|
BuffClass::NANO, // buff class
|
||||||
|
};
|
||||||
|
int timeBuffId = Abilities::getCSTBFromST(skill->skillType);
|
||||||
|
target->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
|
||||||
|
},
|
||||||
|
&debuff);
|
||||||
|
}
|
||||||
|
|
||||||
sSkillResult_Damage_N_Debuff result{};
|
sSkillResult_Damage_N_Debuff result{};
|
||||||
|
result.iDamage = duration / 10; // we use the duration as the damage number (why?)
|
||||||
|
result.iHP = target->getCurrentHP();
|
||||||
result.eCT = target->getCharType();
|
result.eCT = target->getCharType();
|
||||||
result.iID = target->getID();
|
result.iID = target->getID();
|
||||||
result.bProtected = false;
|
result.bProtected = blocked;
|
||||||
result.iConditionBitFlag = target->getCompositeCondition();
|
result.iConditionBitFlag = target->getCompositeCondition();
|
||||||
return SkillResult(sizeof(sSkillResult_Damage_N_Debuff), &result);
|
return SkillResult(sizeof(sSkillResult_Damage_N_Debuff), &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SkillResult handleSkillLeech(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
||||||
|
// TODO abilities
|
||||||
|
return SkillResult();
|
||||||
|
}
|
||||||
|
|
||||||
static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
static SkillResult handleSkillBuff(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
||||||
|
int duration = skill->durationTime[power];
|
||||||
|
int strength = skill->values[0][power];
|
||||||
|
BuffStack passiveBuff = {
|
||||||
|
skill->drainType == SkillDrainType::PASSIVE ? 1 : duration, // ticks
|
||||||
|
strength, // value
|
||||||
|
source->getRef(), // source
|
||||||
|
source == target ? BuffClass::NANO : BuffClass::GROUP_NANO, // buff class
|
||||||
|
};
|
||||||
|
|
||||||
|
int timeBuffId = Abilities::getCSTBFromST(skill->skillType);
|
||||||
|
if(!target->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)) return SkillResult(); // no result if already buffed
|
||||||
|
|
||||||
sSkillResult_Buff result{};
|
sSkillResult_Buff result{};
|
||||||
result.eCT = target->getCharType();
|
result.eCT = target->getCharType();
|
||||||
result.iID = target->getID();
|
result.iID = target->getID();
|
||||||
@ -96,7 +146,13 @@ static SkillResult handleSkillBatteryDrain(SkillData* skill, int power, ICombata
|
|||||||
static SkillResult handleSkillMove(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
static SkillResult handleSkillMove(SkillData* skill, int power, ICombatant* source, ICombatant* target) {
|
||||||
if(source->getCharType() != 1)
|
if(source->getCharType() != 1)
|
||||||
return SkillResult(); // only Players are valid sources for recall
|
return SkillResult(); // only Players are valid sources for recall
|
||||||
|
|
||||||
Player* plr = dynamic_cast<Player*>(source);
|
Player* plr = dynamic_cast<Player*>(source);
|
||||||
|
if(source == target) {
|
||||||
|
// no trailing struct for self
|
||||||
|
PlayerManager::sendPlayerTo(target->getRef().sock, plr->recallX, plr->recallY, plr->recallZ, plr->recallInstance);
|
||||||
|
return SkillResult();
|
||||||
|
}
|
||||||
|
|
||||||
sSkillResult_Move result{};
|
sSkillResult_Move result{};
|
||||||
result.eCT = target->getCharType();
|
result.eCT = target->getCharType();
|
||||||
@ -118,63 +174,70 @@ static SkillResult handleSkillResurrect(SkillData* skill, int power, ICombatant*
|
|||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombatant* src, std::vector<ICombatant*> targets) {
|
static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombatant* src, std::vector<ICombatant*> targets) {
|
||||||
size_t resultSize = 0;
|
|
||||||
SkillResult (*skillHandler)(SkillData*, int, ICombatant*, ICombatant*) = nullptr;
|
SkillResult (*skillHandler)(SkillData*, int, ICombatant*, ICombatant*) = nullptr;
|
||||||
std::vector<SkillResult> results;
|
std::vector<SkillResult> results;
|
||||||
|
|
||||||
switch(skill->skillType)
|
switch(skill->skillType)
|
||||||
{
|
{
|
||||||
case EST_DAMAGE:
|
case SkillType::DAMAGE:
|
||||||
resultSize = sizeof(sSkillResult_Damage);
|
|
||||||
skillHandler = handleSkillDamage;
|
skillHandler = handleSkillDamage;
|
||||||
break;
|
break;
|
||||||
case EST_HEAL_HP:
|
case SkillType::HEAL_HP:
|
||||||
case EST_RETURNHOMEHEAL:
|
case SkillType::RETURNHOMEHEAL:
|
||||||
resultSize = sizeof(sSkillResult_Heal_HP);
|
|
||||||
skillHandler = handleSkillHealHP;
|
skillHandler = handleSkillHealHP;
|
||||||
break;
|
break;
|
||||||
case EST_JUMP:
|
case SkillType::KNOCKDOWN:
|
||||||
case EST_RUN:
|
case SkillType::SLEEP:
|
||||||
case EST_FREEDOM:
|
case SkillType::SNARE:
|
||||||
case EST_PHOENIX:
|
case SkillType::STUN:
|
||||||
case EST_INVULNERABLE:
|
skillHandler = handleSkillDamageNDebuff;
|
||||||
case EST_MINIMAPENEMY:
|
break;
|
||||||
case EST_MINIMAPTRESURE:
|
case SkillType::JUMP:
|
||||||
case EST_NANOSTIMPAK:
|
case SkillType::RUN:
|
||||||
case EST_PROTECTBATTERY:
|
case SkillType::STEALTH:
|
||||||
case EST_PROTECTINFECTION:
|
case SkillType::MINIMAPENEMY:
|
||||||
case EST_REWARDBLOB:
|
case SkillType::MINIMAPTRESURE:
|
||||||
case EST_REWARDCASH:
|
case SkillType::PHOENIX:
|
||||||
case EST_STAMINA_SELF:
|
case SkillType::PROTECTBATTERY:
|
||||||
case EST_STEALTH:
|
case SkillType::PROTECTINFECTION:
|
||||||
resultSize = sizeof(sSkillResult_Buff);
|
case SkillType::REWARDBLOB:
|
||||||
|
case SkillType::REWARDCASH:
|
||||||
|
// case SkillType::INFECTIONDAMAGE:
|
||||||
|
case SkillType::FREEDOM:
|
||||||
|
case SkillType::BOUNDINGBALL:
|
||||||
|
case SkillType::INVULNERABLE:
|
||||||
|
case SkillType::STAMINA_SELF:
|
||||||
|
case SkillType::NANOSTIMPAK:
|
||||||
|
case SkillType::BUFFHEAL:
|
||||||
skillHandler = handleSkillBuff;
|
skillHandler = handleSkillBuff;
|
||||||
break;
|
break;
|
||||||
case EST_BATTERYDRAIN:
|
case SkillType::BLOODSUCKING:
|
||||||
resultSize = sizeof(sSkillResult_BatteryDrain);
|
skillHandler = handleSkillLeech;
|
||||||
skillHandler = handleSkillBatteryDrain;
|
|
||||||
break;
|
break;
|
||||||
case EST_RECALL:
|
case SkillType::RETROROCKET_SELF:
|
||||||
case EST_RECALL_GROUP:
|
// no-op
|
||||||
resultSize = sizeof(sSkillResult_Move);
|
return results;
|
||||||
skillHandler = handleSkillMove;
|
case SkillType::PHOENIX_GROUP:
|
||||||
break;
|
|
||||||
case EST_PHOENIX_GROUP:
|
|
||||||
resultSize = sizeof(sSkillResult_Resurrect);
|
|
||||||
skillHandler = handleSkillResurrect;
|
skillHandler = handleSkillResurrect;
|
||||||
break;
|
break;
|
||||||
|
case SkillType::RECALL:
|
||||||
|
case SkillType::RECALL_GROUP:
|
||||||
|
skillHandler = handleSkillMove;
|
||||||
|
break;
|
||||||
|
case SkillType::BATTERYDRAIN:
|
||||||
|
skillHandler = handleSkillBatteryDrain;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
std::cout << "[WARN] Unhandled skill type " << skill->skillType << std::endl;
|
std::cout << "[WARN] Unhandled skill type " << (int)skill->skillType << std::endl;
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
assert(skillHandler != nullptr);
|
|
||||||
|
|
||||||
for(ICombatant* target : targets) {
|
for(ICombatant* target : targets) {
|
||||||
assert(target != nullptr);
|
assert(target != nullptr);
|
||||||
SkillResult result = skillHandler(skill, power, src != nullptr ? src : target, target);
|
SkillResult result = skillHandler(skill, power, src != nullptr ? src : target, target);
|
||||||
if(result.size == 0) continue; // skill not applicable
|
if(result.size == 0) continue; // skill not applicable
|
||||||
if(result.size != resultSize) {
|
if(result.size > MAX_SKILLRESULT_SIZE) {
|
||||||
std::cout << "[WARN] bad skill result size for " << skill->skillType << " from " << (void*)handleSkillBuff << std::endl;
|
std::cout << "[WARN] bad skill result size for " << (int)skill->skillType << " from " << (void*)handleSkillBuff << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
results.push_back(result);
|
results.push_back(result);
|
||||||
@ -182,30 +245,42 @@ static std::vector<SkillResult> handleSkill(SkillData* skill, int power, ICombat
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void attachSkillResults(std::vector<SkillResult> results, size_t resultSize, uint8_t* pivot) {
|
static void attachSkillResults(std::vector<SkillResult> results, uint8_t* pivot) {
|
||||||
for(SkillResult& result : results) {
|
for(SkillResult& result : results) {
|
||||||
memcpy(pivot, result.payload, resultSize);
|
size_t sz = result.size;
|
||||||
pivot += resultSize;
|
memcpy(pivot, result.payload, sz);
|
||||||
|
pivot += sz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Abilities::useNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICombatant*> affected) {
|
void Abilities::useNanoSkill(CNSocket* sock, SkillData* skill, sNano& nano, std::vector<ICombatant*> affected) {
|
||||||
if(SkillTable.count(nano.iSkillID) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SkillData* skill = &SkillTable[nano.iSkillID];
|
|
||||||
Player* plr = PlayerManager::getPlayer(sock);
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
ICombatant* combatant = dynamic_cast<ICombatant*>(plr);
|
||||||
|
|
||||||
std::vector<SkillResult> results = handleSkill(skill, Nanos::getNanoBoost(plr), plr, affected);
|
int boost = 0;
|
||||||
size_t resultSize = results.back().size; // guaranteed to be the same for every item
|
if (Nanos::getNanoBoost(plr))
|
||||||
|
boost = 3;
|
||||||
|
|
||||||
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), results.size(), resultSize)) {
|
if(skill->drainType == SkillDrainType::ACTIVE) {
|
||||||
|
nano.iStamina -= skill->batteryUse[boost];
|
||||||
|
if (nano.iStamina <= 0)
|
||||||
|
nano.iStamina = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SkillResult> results = handleSkill(skill, boost, combatant, affected);
|
||||||
|
if(results.empty()) return; // no effect; no need for confirmation packets
|
||||||
|
|
||||||
|
// lazy validation since skill results might be different sizes
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC), results.size(), MAX_SKILLRESULT_SIZE)) {
|
||||||
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE_SUCC packet size\n";
|
std::cout << "[WARN] bad sP_FE2CL_NANO_SKILL_USE_SUCC packet size\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize response struct
|
// initialize response struct
|
||||||
size_t resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC) + results.size() * resultSize;
|
size_t resplen = sizeof(sP_FE2CL_NANO_SKILL_USE_SUCC);
|
||||||
|
for(SkillResult& sr : results)
|
||||||
|
resplen += sr.size;
|
||||||
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
memset(respbuf, 0, resplen);
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
@ -215,12 +290,17 @@ void Abilities::useNanoSkill(CNSocket* sock, sNano& nano, std::vector<ICombatant
|
|||||||
pkt->iSkillID = nano.iSkillID;
|
pkt->iSkillID = nano.iSkillID;
|
||||||
pkt->iNanoStamina = nano.iStamina;
|
pkt->iNanoStamina = nano.iStamina;
|
||||||
pkt->bNanoDeactive = nano.iStamina <= 0;
|
pkt->bNanoDeactive = nano.iStamina <= 0;
|
||||||
pkt->eST = skill->skillType;
|
pkt->eST = (int32_t)skill->skillType;
|
||||||
pkt->iTargetCnt = (int32_t)results.size();
|
pkt->iTargetCnt = (int32_t)results.size();
|
||||||
|
|
||||||
attachSkillResults(results, resultSize, (uint8_t*)(pkt + 1));
|
attachSkillResults(results, (uint8_t*)(pkt + 1));
|
||||||
sock->sendPacket(pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
|
sock->sendPacket(pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
|
||||||
PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE_SUCC, resplen);
|
|
||||||
|
if(skill->skillType == SkillType::RECALL_GROUP)
|
||||||
|
// group recall packet is sent only to group members
|
||||||
|
PlayerManager::sendToGroup(sock, pkt, P_FE2CL_NANO_SKILL_USE, resplen);
|
||||||
|
else
|
||||||
|
PlayerManager::sendToViewable(sock, pkt, P_FE2CL_NANO_SKILL_USE, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector<ICombatant*> affected) {
|
void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector<ICombatant*> affected) {
|
||||||
@ -235,43 +315,73 @@ void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector<ICombatant*>
|
|||||||
SkillData* skill = &SkillTable[skillID];
|
SkillData* skill = &SkillTable[skillID];
|
||||||
|
|
||||||
std::vector<SkillResult> results = handleSkill(skill, 0, src, affected);
|
std::vector<SkillResult> results = handleSkill(skill, 0, src, affected);
|
||||||
size_t resultSize = results.back().size; // guaranteed to be the same for every item
|
if(results.empty()) return; // no effect; no need for confirmation packets
|
||||||
|
|
||||||
if (!validOutVarPacket(sizeof(sP_FE2CL_NPC_SKILL_HIT), results.size(), resultSize)) {
|
// lazy validation since skill results might be different sizes
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_NPC_SKILL_HIT), results.size(), MAX_SKILLRESULT_SIZE)) {
|
||||||
std::cout << "[WARN] bad sP_FE2CL_NPC_SKILL_HIT packet size\n";
|
std::cout << "[WARN] bad sP_FE2CL_NPC_SKILL_HIT packet size\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize response struct
|
// initialize response struct
|
||||||
size_t resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + results.size() * resultSize;
|
size_t resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT);
|
||||||
|
for(SkillResult& sr : results)
|
||||||
|
resplen += sr.size;
|
||||||
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
memset(respbuf, 0, resplen);
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
sP_FE2CL_NPC_SKILL_HIT* pkt = (sP_FE2CL_NPC_SKILL_HIT*)respbuf;
|
sP_FE2CL_NPC_SKILL_HIT* pkt = (sP_FE2CL_NPC_SKILL_HIT*)respbuf;
|
||||||
pkt->iNPC_ID = npc.id;
|
pkt->iNPC_ID = npc.id;
|
||||||
pkt->iSkillID = skillID;
|
pkt->iSkillID = skillID;
|
||||||
pkt->eST = skill->skillType;
|
pkt->eST = (int32_t)skill->skillType;
|
||||||
pkt->iTargetCnt = (int32_t)results.size();
|
pkt->iTargetCnt = (int32_t)results.size();
|
||||||
|
|
||||||
attachSkillResults(results, resultSize, (uint8_t*)(pkt + 1));
|
attachSkillResults(results, (uint8_t*)(pkt + 1));
|
||||||
NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen);
|
NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<EntityRef> Abilities::matchTargets(SkillData* skill, int count, int32_t *ids) {
|
static std::vector<ICombatant*> entityRefsToCombatants(std::vector<EntityRef> refs) {
|
||||||
|
std::vector<ICombatant*> combatants;
|
||||||
|
for(EntityRef ref : refs) {
|
||||||
|
if(ref.kind == EntityKind::PLAYER)
|
||||||
|
combatants.push_back(dynamic_cast<ICombatant*>(PlayerManager::getPlayer(ref.sock)));
|
||||||
|
else if(ref.kind == EntityKind::COMBAT_NPC || ref.kind == EntityKind::MOB)
|
||||||
|
combatants.push_back(dynamic_cast<ICombatant*>(ref.getEntity()));
|
||||||
|
}
|
||||||
|
return combatants;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<EntityRef> targets;
|
std::vector<ICombatant*> Abilities::matchTargets(ICombatant* src, SkillData* skill, int count, int32_t *ids) {
|
||||||
|
|
||||||
|
if(skill->targetType == SkillTargetType::GROUP)
|
||||||
|
return entityRefsToCombatants(src->getGroupMembers());
|
||||||
|
|
||||||
|
// this check *has* to happen after the group check above due to cases like group recall that use both
|
||||||
|
if(skill->effectTarget == SkillEffectTarget::SELF)
|
||||||
|
return {src}; // client sends 0 targets for certain self-targeting skills (recall)
|
||||||
|
|
||||||
|
// individuals
|
||||||
|
std::vector<ICombatant*> targets;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int32_t id = ids[i];
|
int32_t id = ids[i];
|
||||||
if (skill->targetType == SkillTargetType::MOBS) {
|
if (skill->targetType == SkillTargetType::MOBS) {
|
||||||
// mob?
|
// mob
|
||||||
if (NPCManager::NPCs.find(id) != NPCManager::NPCs.end()) targets.push_back(id);
|
if (NPCManager::NPCs.find(id) != NPCManager::NPCs.end()) {
|
||||||
else std::cout << "[WARN] skill: id not found\n";
|
BaseNPC* npc = NPCManager::NPCs[id];
|
||||||
} else {
|
if (npc->kind == EntityKind::COMBAT_NPC || npc->kind == EntityKind::MOB) {
|
||||||
// player?
|
targets.push_back(dynamic_cast<ICombatant*>(npc));
|
||||||
CNSocket* sock = PlayerManager::getSockFromID(id);
|
continue;
|
||||||
if (sock != nullptr) targets.push_back(sock);
|
}
|
||||||
else std::cout << "[WARN] skill: sock not found\n";
|
}
|
||||||
|
std::cout << "[WARN] skill: invalid mob target (id " << id << ")\n";
|
||||||
|
} else if(skill->targetType == SkillTargetType::PLAYERS) {
|
||||||
|
// player
|
||||||
|
Player* plr = PlayerManager::getPlayerFromID(id);
|
||||||
|
if (plr != nullptr) {
|
||||||
|
targets.push_back(dynamic_cast<ICombatant*>(plr));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << "[WARN] skill: invalid player target (id " << id << ")\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,64 +389,66 @@ std::vector<EntityRef> Abilities::matchTargets(SkillData* skill, int count, int3
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ripped from client (enums emplaced) */
|
/* ripped from client (enums emplaced) */
|
||||||
int Abilities::getCSTBFromST(int eSkillType) {
|
int Abilities::getCSTBFromST(SkillType skillType) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
switch (eSkillType)
|
switch (skillType)
|
||||||
{
|
{
|
||||||
case EST_RUN:
|
case SkillType::RUN:
|
||||||
result = ECSB_UP_MOVE_SPEED;
|
result = ECSB_UP_MOVE_SPEED;
|
||||||
break;
|
break;
|
||||||
case EST_JUMP:
|
case SkillType::JUMP:
|
||||||
result = ECSB_UP_JUMP_HEIGHT;
|
result = ECSB_UP_JUMP_HEIGHT;
|
||||||
break;
|
break;
|
||||||
case EST_STEALTH:
|
case SkillType::STEALTH:
|
||||||
result = ECSB_UP_STEALTH;
|
result = ECSB_UP_STEALTH;
|
||||||
break;
|
break;
|
||||||
case EST_PHOENIX:
|
case SkillType::PHOENIX:
|
||||||
result = ECSB_PHOENIX;
|
result = ECSB_PHOENIX;
|
||||||
break;
|
break;
|
||||||
case EST_PROTECTBATTERY:
|
case SkillType::PROTECTBATTERY:
|
||||||
result = ECSB_PROTECT_BATTERY;
|
result = ECSB_PROTECT_BATTERY;
|
||||||
break;
|
break;
|
||||||
case EST_PROTECTINFECTION:
|
case SkillType::PROTECTINFECTION:
|
||||||
result = ECSB_PROTECT_INFECTION;
|
result = ECSB_PROTECT_INFECTION;
|
||||||
break;
|
break;
|
||||||
case EST_SNARE:
|
case SkillType::SNARE:
|
||||||
result = ECSB_DN_MOVE_SPEED;
|
result = ECSB_DN_MOVE_SPEED;
|
||||||
break;
|
break;
|
||||||
case EST_SLEEP:
|
case SkillType::SLEEP:
|
||||||
result = ECSB_MEZ;
|
result = ECSB_MEZ;
|
||||||
break;
|
break;
|
||||||
case EST_MINIMAPENEMY:
|
case SkillType::MINIMAPENEMY:
|
||||||
result = ECSB_MINIMAP_ENEMY;
|
result = ECSB_MINIMAP_ENEMY;
|
||||||
break;
|
break;
|
||||||
case EST_MINIMAPTRESURE:
|
case SkillType::MINIMAPTRESURE:
|
||||||
result = ECSB_MINIMAP_TRESURE;
|
result = ECSB_MINIMAP_TRESURE;
|
||||||
break;
|
break;
|
||||||
case EST_REWARDBLOB:
|
case SkillType::REWARDBLOB:
|
||||||
result = ECSB_REWARD_BLOB;
|
result = ECSB_REWARD_BLOB;
|
||||||
break;
|
break;
|
||||||
case EST_REWARDCASH:
|
case SkillType::REWARDCASH:
|
||||||
result = ECSB_REWARD_CASH;
|
result = ECSB_REWARD_CASH;
|
||||||
break;
|
break;
|
||||||
case EST_INFECTIONDAMAGE:
|
case SkillType::INFECTIONDAMAGE:
|
||||||
result = ECSB_INFECTION;
|
result = ECSB_INFECTION;
|
||||||
break;
|
break;
|
||||||
case EST_FREEDOM:
|
case SkillType::FREEDOM:
|
||||||
result = ECSB_FREEDOM;
|
result = ECSB_FREEDOM;
|
||||||
break;
|
break;
|
||||||
case EST_BOUNDINGBALL:
|
case SkillType::BOUNDINGBALL:
|
||||||
result = ECSB_BOUNDINGBALL;
|
result = ECSB_BOUNDINGBALL;
|
||||||
break;
|
break;
|
||||||
case EST_INVULNERABLE:
|
case SkillType::INVULNERABLE:
|
||||||
result = ECSB_INVULNERABLE;
|
result = ECSB_INVULNERABLE;
|
||||||
break;
|
break;
|
||||||
case EST_BUFFHEAL:
|
case SkillType::BUFFHEAL:
|
||||||
result = ECSB_HEAL;
|
result = ECSB_HEAL;
|
||||||
break;
|
break;
|
||||||
case EST_NANOSTIMPAK:
|
case SkillType::NANOSTIMPAK:
|
||||||
result = ECSB_STIMPAKSLOT1;
|
result = ECSB_STIMPAKSLOT1;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,51 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
constexpr size_t MAX_SKILLRESULT_SIZE = sizeof(sSkillResult_BatteryDrain);
|
constexpr size_t MAX_SKILLRESULT_SIZE = sizeof(sSkillResult_BatteryDrain);
|
||||||
|
|
||||||
|
enum class SkillType {
|
||||||
|
DAMAGE = 1,
|
||||||
|
HEAL_HP = 2,
|
||||||
|
KNOCKDOWN = 3, // dnd
|
||||||
|
SLEEP = 4, // dnd
|
||||||
|
SNARE = 5, // dnd
|
||||||
|
HEAL_STAMINA = 6,
|
||||||
|
STAMINA_SELF = 7,
|
||||||
|
STUN = 8, // dnd
|
||||||
|
WEAPONSLOW = 9,
|
||||||
|
JUMP = 10,
|
||||||
|
RUN = 11,
|
||||||
|
STEALTH = 12,
|
||||||
|
SWIM = 13,
|
||||||
|
MINIMAPENEMY = 14,
|
||||||
|
MINIMAPTRESURE = 15,
|
||||||
|
PHOENIX = 16,
|
||||||
|
PROTECTBATTERY = 17,
|
||||||
|
PROTECTINFECTION = 18,
|
||||||
|
REWARDBLOB = 19,
|
||||||
|
REWARDCASH = 20,
|
||||||
|
BATTERYDRAIN = 21,
|
||||||
|
CORRUPTIONATTACK = 22,
|
||||||
|
INFECTIONDAMAGE = 23,
|
||||||
|
KNOCKBACK = 24,
|
||||||
|
FREEDOM = 25,
|
||||||
|
PHOENIX_GROUP = 26,
|
||||||
|
RECALL = 27,
|
||||||
|
RECALL_GROUP = 28,
|
||||||
|
RETROROCKET_SELF = 29,
|
||||||
|
BLOODSUCKING = 30,
|
||||||
|
BOUNDINGBALL = 31,
|
||||||
|
INVULNERABLE = 32,
|
||||||
|
NANOSTIMPAK = 33,
|
||||||
|
RETURNHOMEHEAL = 34,
|
||||||
|
BUFFHEAL = 35,
|
||||||
|
EXTRABANK = 36,
|
||||||
|
CORRUPTIONATTACKWIN = 38,
|
||||||
|
CORRUPTIONATTACKLOSE = 39,
|
||||||
|
};
|
||||||
|
|
||||||
enum class SkillEffectTarget {
|
enum class SkillEffectTarget {
|
||||||
POINT = 1,
|
POINT = 1,
|
||||||
SELF = 2,
|
SELF = 2,
|
||||||
@ -21,7 +63,7 @@ enum class SkillEffectTarget {
|
|||||||
|
|
||||||
enum class SkillTargetType {
|
enum class SkillTargetType {
|
||||||
MOBS = 1,
|
MOBS = 1,
|
||||||
SELF = 2,
|
PLAYERS = 2,
|
||||||
GROUP = 3
|
GROUP = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,6 +76,7 @@ struct SkillResult {
|
|||||||
size_t size;
|
size_t size;
|
||||||
uint8_t payload[MAX_SKILLRESULT_SIZE];
|
uint8_t payload[MAX_SKILLRESULT_SIZE];
|
||||||
SkillResult(size_t len, void* dat) {
|
SkillResult(size_t len, void* dat) {
|
||||||
|
assert(len <= MAX_SKILLRESULT_SIZE);
|
||||||
size = len;
|
size = len;
|
||||||
memcpy(payload, dat, len);
|
memcpy(payload, dat, len);
|
||||||
}
|
}
|
||||||
@ -43,7 +86,7 @@ struct SkillResult {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SkillData {
|
struct SkillData {
|
||||||
int skillType; // eST
|
SkillType skillType; // eST
|
||||||
SkillEffectTarget effectTarget;
|
SkillEffectTarget effectTarget;
|
||||||
int effectType; // always 1?
|
int effectType; // always 1?
|
||||||
SkillTargetType targetType;
|
SkillTargetType targetType;
|
||||||
@ -60,9 +103,9 @@ struct SkillData {
|
|||||||
namespace Abilities {
|
namespace Abilities {
|
||||||
extern std::map<int32_t, SkillData> SkillTable;
|
extern std::map<int32_t, SkillData> SkillTable;
|
||||||
|
|
||||||
void useNanoSkill(CNSocket*, sNano&, std::vector<ICombatant*>);
|
void useNanoSkill(CNSocket*, SkillData*, sNano&, std::vector<ICombatant*>);
|
||||||
void useNPCSkill(EntityRef, int skillID, std::vector<ICombatant*>);
|
void useNPCSkill(EntityRef, int skillID, std::vector<ICombatant*>);
|
||||||
|
|
||||||
std::vector<EntityRef> matchTargets(SkillData*, int, int32_t*);
|
std::vector<ICombatant*> matchTargets(ICombatant*, SkillData*, int, int32_t*);
|
||||||
int getCSTBFromST(int eSkillType);
|
int getCSTBFromST(SkillType skillType);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,9 @@ std::map<int32_t, std::map<int8_t, Bullet>> Combat::Bullets;
|
|||||||
|
|
||||||
#pragma region Player
|
#pragma region Player
|
||||||
bool Player::addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) {
|
bool Player::addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, BuffStack* stack) {
|
||||||
|
if(!isAlive())
|
||||||
|
return false;
|
||||||
|
|
||||||
EntityRef self = PlayerManager::getSockFromID(iID);
|
EntityRef self = PlayerManager::getSockFromID(iID);
|
||||||
|
|
||||||
if(!hasBuff(buffId)) {
|
if(!hasBuff(buffId)) {
|
||||||
@ -51,6 +54,7 @@ void Player::removeBuff(int buffId) {
|
|||||||
void Player::removeBuff(int buffId, int buffClass) {
|
void Player::removeBuff(int buffId, int buffClass) {
|
||||||
if(hasBuff(buffId)) {
|
if(hasBuff(buffId)) {
|
||||||
buffs[buffId]->clear((BuffClass)buffClass);
|
buffs[buffId]->clear((BuffClass)buffClass);
|
||||||
|
// buff might not be stale since another buff class might remain
|
||||||
if(buffs[buffId]->isStale()) {
|
if(buffs[buffId]->isStale()) {
|
||||||
delete buffs[buffId];
|
delete buffs[buffId];
|
||||||
buffs.erase(buffId);
|
buffs.erase(buffId);
|
||||||
@ -58,6 +62,17 @@ void Player::removeBuff(int buffId, int buffClass) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::clearBuffs(bool force) {
|
||||||
|
for(auto buff : buffs) {
|
||||||
|
if(!force) {
|
||||||
|
removeBuff(buff.first);
|
||||||
|
} else {
|
||||||
|
delete buff.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
bool Player::hasBuff(int buffId) {
|
bool Player::hasBuff(int buffId) {
|
||||||
auto buff = buffs.find(buffId);
|
auto buff = buffs.find(buffId);
|
||||||
return buff != buffs.end() && !buff->second->isStale();
|
return buff != buffs.end() && !buff->second->isStale();
|
||||||
@ -173,6 +188,8 @@ void CombatNPC::removeBuff(int buffId) { /* stubbed */ }
|
|||||||
|
|
||||||
void CombatNPC::removeBuff(int buffId, int buffClass) { /* stubbed */ }
|
void CombatNPC::removeBuff(int buffId, int buffClass) { /* stubbed */ }
|
||||||
|
|
||||||
|
void CombatNPC::clearBuffs(bool force) { /* stubbed */ }
|
||||||
|
|
||||||
bool CombatNPC::hasBuff(int buffId) { /* stubbed */
|
bool CombatNPC::hasBuff(int buffId) { /* stubbed */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -874,9 +891,12 @@ static void playerTick(CNServer *serv, time_t currTime) {
|
|||||||
if (Abilities::SkillTable.find(nano->iSkillID) != Abilities::SkillTable.end()) {
|
if (Abilities::SkillTable.find(nano->iSkillID) != Abilities::SkillTable.end()) {
|
||||||
// nano has skill data
|
// nano has skill data
|
||||||
SkillData* skill = &Abilities::SkillTable[nano->iSkillID];
|
SkillData* skill = &Abilities::SkillTable[nano->iSkillID];
|
||||||
if (skill->drainType == SkillDrainType::PASSIVE)
|
if (skill->drainType == SkillDrainType::PASSIVE) {
|
||||||
Nanos::applyNanoBuff(skill, plr);
|
ICombatant* src = dynamic_cast<ICombatant*>(plr);
|
||||||
// ^ composite condition calculation is separate from combat for responsiveness
|
int32_t targets[] = { plr->iID };
|
||||||
|
std::vector<ICombatant*> affectedCombatants = Abilities::matchTargets(src, skill, 1, targets);
|
||||||
|
Abilities::useNanoSkill(sock, skill, *nano, affectedCombatants);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,7 +678,6 @@ static void whoisCommand(std::string full, std::vector<std::string>& args, CNSoc
|
|||||||
Chat::sendServerMessage(sock, "[WHOIS] ID: " + std::to_string(npc->id));
|
Chat::sendServerMessage(sock, "[WHOIS] ID: " + std::to_string(npc->id));
|
||||||
Chat::sendServerMessage(sock, "[WHOIS] Type: " + std::to_string(npc->type));
|
Chat::sendServerMessage(sock, "[WHOIS] Type: " + std::to_string(npc->type));
|
||||||
Chat::sendServerMessage(sock, "[WHOIS] HP: " + std::to_string(npc->hp));
|
Chat::sendServerMessage(sock, "[WHOIS] HP: " + std::to_string(npc->hp));
|
||||||
Chat::sendServerMessage(sock, "[WHOIS] CBF: " + std::to_string(npc->cbf));
|
|
||||||
Chat::sendServerMessage(sock, "[WHOIS] EntityType: " + std::to_string((int)npc->kind));
|
Chat::sendServerMessage(sock, "[WHOIS] EntityType: " + std::to_string((int)npc->kind));
|
||||||
Chat::sendServerMessage(sock, "[WHOIS] X: " + std::to_string(npc->x));
|
Chat::sendServerMessage(sock, "[WHOIS] X: " + std::to_string(npc->x));
|
||||||
Chat::sendServerMessage(sock, "[WHOIS] Y: " + std::to_string(npc->y));
|
Chat::sendServerMessage(sock, "[WHOIS] Y: " + std::to_string(npc->y));
|
||||||
|
@ -29,7 +29,7 @@ void Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) {
|
|||||||
SkillData* skill = &Abilities::SkillTable[skillId];
|
SkillData* skill = &Abilities::SkillTable[skillId];
|
||||||
if(skill->drainType == SkillDrainType::PASSIVE) {
|
if(skill->drainType == SkillDrainType::PASSIVE) {
|
||||||
// apply buff
|
// apply buff
|
||||||
if(skill->targetType != SkillTargetType::SELF) {
|
if(skill->targetType != SkillTargetType::PLAYERS) {
|
||||||
std::cout << "[WARN] weird skill type for egg " << eggId << " with skill " << skillId << ", should be " << (int)skill->targetType << std::endl;
|
std::cout << "[WARN] weird skill type for egg " << eggId << " with skill " << skillId << ", should be " << (int)skill->targetType << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ sNPCAppearanceData BaseNPC::getAppearanceData() {
|
|||||||
sNPCAppearanceData data = {};
|
sNPCAppearanceData data = {};
|
||||||
data.iAngle = angle;
|
data.iAngle = angle;
|
||||||
data.iBarkerType = 0; // unused?
|
data.iBarkerType = 0; // unused?
|
||||||
data.iConditionBitFlag = cbf;
|
data.iConditionBitFlag = 0;
|
||||||
data.iHP = hp;
|
data.iHP = hp;
|
||||||
data.iNPCType = type;
|
data.iNPCType = type;
|
||||||
data.iNPC_ID = id;
|
data.iNPC_ID = id;
|
||||||
@ -50,6 +50,12 @@ sNPCAppearanceData BaseNPC::getAppearanceData() {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sNPCAppearanceData CombatNPC::getAppearanceData() {
|
||||||
|
sNPCAppearanceData data = BaseNPC::getAppearanceData();
|
||||||
|
data.iConditionBitFlag = getCompositeCondition();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Entity coming into view.
|
* Entity coming into view.
|
||||||
*/
|
*/
|
||||||
|
@ -48,6 +48,7 @@ public:
|
|||||||
virtual Buff* getBuff(int) = 0;
|
virtual Buff* getBuff(int) = 0;
|
||||||
virtual void removeBuff(int) = 0;
|
virtual void removeBuff(int) = 0;
|
||||||
virtual void removeBuff(int, int) = 0;
|
virtual void removeBuff(int, int) = 0;
|
||||||
|
virtual void clearBuffs(bool) = 0;
|
||||||
virtual bool hasBuff(int) = 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;
|
||||||
@ -72,7 +73,6 @@ public:
|
|||||||
int type;
|
int type;
|
||||||
int hp;
|
int hp;
|
||||||
int angle;
|
int angle;
|
||||||
int cbf;
|
|
||||||
bool loopingPath = false;
|
bool loopingPath = false;
|
||||||
|
|
||||||
BaseNPC(int _A, uint64_t iID, int t, int _id) {
|
BaseNPC(int _A, uint64_t iID, int t, int _id) {
|
||||||
@ -80,7 +80,6 @@ public:
|
|||||||
type = t;
|
type = t;
|
||||||
hp = 400;
|
hp = 400;
|
||||||
angle = _A;
|
angle = _A;
|
||||||
cbf = 0;
|
|
||||||
id = _id;
|
id = _id;
|
||||||
instanceID = iID;
|
instanceID = iID;
|
||||||
};
|
};
|
||||||
@ -88,7 +87,7 @@ public:
|
|||||||
virtual void enterIntoViewOf(CNSocket *sock) override;
|
virtual void enterIntoViewOf(CNSocket *sock) override;
|
||||||
virtual void disappearFromViewOf(CNSocket *sock) override;
|
virtual void disappearFromViewOf(CNSocket *sock) override;
|
||||||
|
|
||||||
sNPCAppearanceData getAppearanceData();
|
virtual sNPCAppearanceData getAppearanceData();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CombatNPC : public BaseNPC, public ICombatant {
|
struct CombatNPC : public BaseNPC, public ICombatant {
|
||||||
@ -105,6 +104,8 @@ struct CombatNPC : public BaseNPC, public ICombatant {
|
|||||||
std::map<AIState, void (*)(CombatNPC*, time_t)> stateHandlers;
|
std::map<AIState, void (*)(CombatNPC*, time_t)> stateHandlers;
|
||||||
std::map<AIState, void (*)(CombatNPC*, EntityRef)> transitionHandlers;
|
std::map<AIState, void (*)(CombatNPC*, EntityRef)> transitionHandlers;
|
||||||
|
|
||||||
|
std::unordered_map<int, Buff*> buffs = {};
|
||||||
|
|
||||||
CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP)
|
CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP)
|
||||||
: BaseNPC(angle, iID, t, id), maxHealth(maxHP) {
|
: BaseNPC(angle, iID, t, id), maxHealth(maxHP) {
|
||||||
spawnX = x;
|
spawnX = x;
|
||||||
@ -117,12 +118,15 @@ struct CombatNPC : public BaseNPC, public ICombatant {
|
|||||||
transitionHandlers[AIState::INACTIVE] = {};
|
transitionHandlers[AIState::INACTIVE] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual sNPCAppearanceData getAppearanceData() override;
|
||||||
|
|
||||||
virtual bool isExtant() override { return hp > 0; }
|
virtual bool isExtant() override { return hp > 0; }
|
||||||
|
|
||||||
virtual bool addBuff(int buffId, BuffCallback<int, BuffStack*> onUpdate, BuffCallback<time_t> onTick, 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 void removeBuff(int buffId, int buffClass) override;
|
virtual void removeBuff(int buffId, int buffClass) override;
|
||||||
|
virtual void clearBuffs(bool force) override;
|
||||||
virtual bool hasBuff(int buffId) override;
|
virtual bool hasBuff(int buffId) override;
|
||||||
virtual int getCompositeCondition() override;
|
virtual int getCompositeCondition() override;
|
||||||
virtual int takeDamage(EntityRef src, int amt) override;
|
virtual int takeDamage(EntityRef src, int amt) override;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "EntityRef.hpp"
|
#include "EntityRef.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
struct Group {
|
struct Group {
|
||||||
std::vector<EntityRef> members;
|
std::vector<EntityRef> members;
|
||||||
@ -14,6 +15,10 @@ struct Group {
|
|||||||
});
|
});
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
EntityRef getLeader() {
|
||||||
|
assert(members.size() > 0);
|
||||||
|
return members[0];
|
||||||
|
}
|
||||||
|
|
||||||
Group(EntityRef leader);
|
Group(EntityRef leader);
|
||||||
};
|
};
|
||||||
|
@ -482,7 +482,7 @@ static void itemUseHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
resp->iSlotNum = request->iSlotNum;
|
resp->iSlotNum = request->iSlotNum;
|
||||||
resp->RemainItem = gumball;
|
resp->RemainItem = gumball;
|
||||||
resp->iTargetCnt = 1;
|
resp->iTargetCnt = 1;
|
||||||
resp->eST = EST_NANOSTIMPAK;
|
resp->eST = (int32_t)SkillType::NANOSTIMPAK;
|
||||||
resp->iSkillID = 144;
|
resp->iSkillID = 144;
|
||||||
|
|
||||||
int eCSB = ECSB_STIMPAKSLOT1 + request->iNanoSlot;
|
int eCSB = ECSB_STIMPAKSLOT1 + request->iNanoSlot;
|
||||||
|
@ -51,13 +51,13 @@ int Mob::takeDamage(EntityRef src, int amt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wake up sleeping monster
|
// wake up sleeping monster
|
||||||
if (cbf & CSB_BIT_MEZ) {
|
if (hasBuff(ECSB_MEZ)) {
|
||||||
cbf &= ~CSB_BIT_MEZ;
|
removeBuff(ECSB_MEZ);
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
|
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
|
||||||
pkt1.eCT = 2;
|
pkt1.eCT = 2;
|
||||||
pkt1.iID = id;
|
pkt1.iID = id;
|
||||||
pkt1.iConditionBitFlag = cbf;
|
pkt1.iConditionBitFlag = getCompositeCondition();
|
||||||
NPCManager::sendToViewable(this, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
|
NPCManager::sendToViewable(this, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,13 +96,12 @@ static std::pair<int,int> lerp(int x1, int y1, int x2, int y2, int speed) {
|
|||||||
|
|
||||||
void MobAI::clearDebuff(Mob *mob) {
|
void MobAI::clearDebuff(Mob *mob) {
|
||||||
mob->skillStyle = -1;
|
mob->skillStyle = -1;
|
||||||
mob->cbf = 0;
|
mob->clearBuffs(false);
|
||||||
mob->unbuffTimes.clear();
|
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
|
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
|
||||||
pkt1.eCT = 2;
|
pkt1.eCT = 2;
|
||||||
pkt1.iID = mob->id;
|
pkt1.iID = mob->id;
|
||||||
pkt1.iConditionBitFlag = mob->cbf;
|
pkt1.iConditionBitFlag = mob->getCompositeCondition();
|
||||||
NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
|
NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,6 +441,7 @@ static void useAbilities(Mob *mob, time_t currTime) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO abiilities
|
||||||
static void drainMobHP(Mob *mob, int amount) {
|
static void drainMobHP(Mob *mob, int amount) {
|
||||||
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Damage);
|
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Damage);
|
||||||
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||||
@ -553,9 +553,9 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// drain
|
// drain TODO abilities
|
||||||
if (self->skillStyle < 0 && (self->lastDrainTime == 0 || currTime - self->lastDrainTime >= 1000)
|
if (self->skillStyle < 0 && (self->lastDrainTime == 0 || currTime - self->lastDrainTime >= 1000)
|
||||||
&& self->cbf & CSB_BIT_BOUNDINGBALL) {
|
&& self->hasBuff(ECSB_BOUNDINGBALL)) {
|
||||||
drainMobHP(self, self->maxHealth / 20); // lose 5% every second
|
drainMobHP(self, self->maxHealth / 20); // lose 5% every second
|
||||||
self->lastDrainTime = currTime;
|
self->lastDrainTime = currTime;
|
||||||
}
|
}
|
||||||
@ -564,27 +564,13 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) {
|
|||||||
if (self->hp <= 0)
|
if (self->hp <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// unbuffing
|
// tick buffs
|
||||||
std::unordered_map<int32_t, time_t>::iterator it = self->unbuffTimes.begin();
|
for(auto buffEntry : self->buffs) {
|
||||||
while (it != self->unbuffTimes.end()) {
|
buffEntry.second->combatTick(currTime);
|
||||||
|
|
||||||
if (currTime >= it->second) {
|
|
||||||
self->cbf &= ~it->first;
|
|
||||||
|
|
||||||
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
|
|
||||||
pkt1.eCT = 2;
|
|
||||||
pkt1.iID = self->id;
|
|
||||||
pkt1.iConditionBitFlag = self->cbf;
|
|
||||||
NPCManager::sendToViewable(self, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
|
|
||||||
|
|
||||||
it = self->unbuffTimes.erase(it);
|
|
||||||
} else {
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip attack if stunned or asleep
|
// skip attack if stunned or asleep
|
||||||
if (self->cbf & (CSB_BIT_STUN|CSB_BIT_MEZ)) {
|
if (self->hasBuff(ECSB_STUN) || self->hasBuff(ECSB_MEZ)) {
|
||||||
self->skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up.
|
self->skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -609,7 +595,7 @@ void MobAI::combatStep(CombatNPC* npc, time_t currTime) {
|
|||||||
self->nextAttack = 0;
|
self->nextAttack = 0;
|
||||||
|
|
||||||
// halve movement speed if snared
|
// halve movement speed if snared
|
||||||
if (self->cbf & CSB_BIT_DN_MOVE_SPEED)
|
if (self->hasBuff(ECSB_DN_MOVE_SPEED))
|
||||||
self->speed /= 2;
|
self->speed /= 2;
|
||||||
|
|
||||||
int targetX = plr->x;
|
int targetX = plr->x;
|
||||||
@ -715,7 +701,7 @@ void MobAI::roamingStep(CombatNPC* npc, time_t currTime) {
|
|||||||
farY = std::clamp(farY, yStart, yStart + self->idleRange);
|
farY = std::clamp(farY, yStart, yStart + self->idleRange);
|
||||||
|
|
||||||
// halve movement speed if snared
|
// halve movement speed if snared
|
||||||
if (self->cbf & CSB_BIT_DN_MOVE_SPEED)
|
if (self->hasBuff(ECSB_DN_MOVE_SPEED))
|
||||||
self->speed /= 2;
|
self->speed /= 2;
|
||||||
|
|
||||||
std::queue<Vec3> queue;
|
std::queue<Vec3> queue;
|
||||||
@ -788,7 +774,6 @@ void MobAI::onRoamStart(CombatNPC* npc, EntityRef src) {
|
|||||||
self->hp = self->maxHealth;
|
self->hp = self->maxHealth;
|
||||||
self->killedTime = 0;
|
self->killedTime = 0;
|
||||||
self->nextAttack = 0;
|
self->nextAttack = 0;
|
||||||
self->cbf = 0;
|
|
||||||
|
|
||||||
// cast a return home heal spell, this is the right way(tm)
|
// cast a return home heal spell, this is the right way(tm)
|
||||||
// TODO ABILITIES
|
// TODO ABILITIES
|
||||||
@ -833,9 +818,8 @@ void MobAI::onDeath(CombatNPC* npc, EntityRef src) {
|
|||||||
Mob* self = (Mob*)npc;
|
Mob* self = (Mob*)npc;
|
||||||
|
|
||||||
self->target = nullptr;
|
self->target = nullptr;
|
||||||
self->cbf = 0;
|
|
||||||
self->skillStyle = -1;
|
self->skillStyle = -1;
|
||||||
self->unbuffTimes.clear();
|
self->clearBuffs(true);
|
||||||
self->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
|
self->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
|
||||||
|
|
||||||
// check for the edge case where hitting the mob did not aggro it
|
// check for the edge case where hitting the mob did not aggro it
|
||||||
|
@ -21,8 +21,6 @@ namespace MobAI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Mob : public CombatNPC {
|
struct Mob : public CombatNPC {
|
||||||
// general
|
|
||||||
std::unordered_map<int32_t,time_t> unbuffTimes = {};
|
|
||||||
|
|
||||||
// dead
|
// dead
|
||||||
time_t killedTime = 0;
|
time_t killedTime = 0;
|
||||||
@ -71,8 +69,6 @@ struct Mob : public CombatNPC {
|
|||||||
offsetX = 0;
|
offsetX = 0;
|
||||||
offsetY = 0;
|
offsetY = 0;
|
||||||
|
|
||||||
cbf = 0;
|
|
||||||
|
|
||||||
// NOTE: there appear to be discrepancies in the dump
|
// NOTE: there appear to be discrepancies in the dump
|
||||||
hp = maxHealth;
|
hp = maxHealth;
|
||||||
|
|
||||||
|
@ -69,52 +69,6 @@ void Nanos::addNano(CNSocket* sock, int16_t nanoID, int16_t slot, bool spendfm)
|
|||||||
PlayerManager::sendToViewable(sock, resp2, P_FE2CL_REP_PC_CHANGE_LEVEL);
|
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) {
|
void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
|
||||||
INITSTRUCT(sP_FE2CL_REP_NANO_ACTIVE_SUCC, resp);
|
INITSTRUCT(sP_FE2CL_REP_NANO_ACTIVE_SUCC, resp);
|
||||||
resp.iActiveNanoSlotNum = slot;
|
resp.iActiveNanoSlotNum = slot;
|
||||||
@ -139,8 +93,10 @@ void Nanos::summonNano(CNSocket *sock, int slot, bool silent) {
|
|||||||
if (slot != -1 && skill != nullptr && skill->drainType == SkillDrainType::PASSIVE) {
|
if (slot != -1 && skill != nullptr && skill->drainType == SkillDrainType::PASSIVE) {
|
||||||
// passive buff effect
|
// passive buff effect
|
||||||
resp.eCSTB___Add = 1;
|
resp.eCSTB___Add = 1;
|
||||||
std::vector<ICombatant*> affectedCombatants = applyNanoBuff(skill, plr);
|
ICombatant* src = dynamic_cast<ICombatant*>(plr);
|
||||||
if(!affectedCombatants.empty()) Abilities::useNanoSkill(sock, nano, affectedCombatants);
|
int32_t targets[] = { plr->iID };
|
||||||
|
std::vector<ICombatant*> affectedCombatants = Abilities::matchTargets(src, skill, 1, targets);
|
||||||
|
Abilities::useNanoSkill(sock, skill, nano, affectedCombatants);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!silent) // silent nano death but only for the summoning player
|
if (!silent) // silent nano death but only for the summoning player
|
||||||
@ -309,28 +265,19 @@ static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t skillID = plr->Nanos[plr->activeNano].iSkillID;
|
sNano& nano = plr->Nanos[plr->activeNano];
|
||||||
|
int16_t skillID = nano.iSkillID;
|
||||||
SkillData* skillData = &Abilities::SkillTable[skillID];
|
SkillData* skillData = &Abilities::SkillTable[skillID];
|
||||||
|
|
||||||
DEBUGLOG(
|
DEBUGLOG(
|
||||||
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl;
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl;
|
||||||
)
|
)
|
||||||
|
|
||||||
int boost = 0;
|
ICombatant* plrCombatant = dynamic_cast<ICombatant*>(plr);
|
||||||
if (getNanoBoost(plr))
|
std::vector<ICombatant*> targetData = Abilities::matchTargets(plrCombatant, skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1));
|
||||||
boost = 1;
|
Abilities::useNanoSkill(sock, skillData, nano, targetData);
|
||||||
|
|
||||||
plr->Nanos[plr->activeNano].iStamina -= Abilities::SkillTable[skillID].batteryUse[boost*3];
|
if (plr->Nanos[plr->activeNano].iStamina <= 0)
|
||||||
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
|
||||||
plr->Nanos[plr->activeNano].iStamina = 0;
|
|
||||||
|
|
||||||
// TODO ABILITIES
|
|
||||||
std::vector<EntityRef> targetData = Abilities::matchTargets(skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1));
|
|
||||||
/*for (auto& pwr : Abilities::Powers)
|
|
||||||
if (pwr.skillType == Abilities::SkillTable[skillID].skillType)
|
|
||||||
pwr.handle(sock, targetData, nanoID, skillID, Abilities::SkillTable[skillID].durationTime[boost], Abilities::SkillTable[skillID].powerIntensity[boost]);*/
|
|
||||||
|
|
||||||
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
|
||||||
summonNano(sock, -1);
|
summonNano(sock, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,5 +26,4 @@ namespace Nanos {
|
|||||||
void summonNano(CNSocket* sock, int slot, bool silent = false);
|
void summonNano(CNSocket* sock, int slot, bool silent = false);
|
||||||
int nanoStyle(int nanoID);
|
int nanoStyle(int nanoID);
|
||||||
bool getNanoBoost(Player* plr);
|
bool getNanoBoost(Player* plr);
|
||||||
std::vector<ICombatant*> applyNanoBuff(SkillData* skill, Player* plr);
|
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ struct Player : public Entity, public ICombatant {
|
|||||||
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 void removeBuff(int buffId, int buffClass) override;
|
virtual void removeBuff(int buffId, int buffClass) override;
|
||||||
|
virtual void clearBuffs(bool force) override;
|
||||||
virtual bool hasBuff(int buffId) override;
|
virtual bool hasBuff(int buffId) override;
|
||||||
virtual int getCompositeCondition() override;
|
virtual int getCompositeCondition() override;
|
||||||
virtual int takeDamage(EntityRef src, int amt) override;
|
virtual int takeDamage(EntityRef src, int amt) override;
|
||||||
|
@ -338,6 +338,14 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
delete lm;
|
delete lm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerManager::sendToGroup(CNSocket* sock, void* buf, uint32_t type, size_t size) {
|
||||||
|
Player* plr = getPlayer(sock);
|
||||||
|
if (plr->group == nullptr)
|
||||||
|
return;
|
||||||
|
for(const EntityRef& ref : plr->group->filter(EntityKind::PLAYER))
|
||||||
|
ref.sock->sendPacket(buf, type, size);
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) {
|
void PlayerManager::sendToViewable(CNSocket* sock, void* buf, uint32_t type, size_t size) {
|
||||||
Player* plr = getPlayer(sock);
|
Player* plr = getPlayer(sock);
|
||||||
for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) {
|
for (auto it = plr->viewableChunks.begin(); it != plr->viewableChunks.end(); it++) {
|
||||||
@ -401,12 +409,11 @@ static void revivePlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
if (!(plr->hasBuff(ECSB_PHOENIX)))
|
if (!(plr->hasBuff(ECSB_PHOENIX)))
|
||||||
return; // sanity check
|
return; // sanity check
|
||||||
plr->Nanos[plr->activeNano].iStamina = 0;
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
// TODO ABILITIES
|
|
||||||
//Abilities::applyBuff(sock, plr->Nanos[plr->activeNano].iSkillID, 2, 1, 0);
|
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case ePCRegenType::HereByPhoenixGroup: // revived by group member's nano
|
case ePCRegenType::HereByPhoenixGroup: // revived by group member's nano
|
||||||
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: // plain respawn
|
default: // plain respawn
|
||||||
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
||||||
// fallthrough
|
// fallthrough
|
||||||
|
@ -33,6 +33,7 @@ namespace PlayerManager {
|
|||||||
CNSocket *getSockFromAny(int by, int id, int uid, std::string firstname, std::string lastname);
|
CNSocket *getSockFromAny(int by, int id, int uid, std::string firstname, std::string lastname);
|
||||||
WarpLocation *getRespawnPoint(Player *plr);
|
WarpLocation *getRespawnPoint(Player *plr);
|
||||||
|
|
||||||
|
void sendToGroup(CNSocket *sock, void* buf, uint32_t type, size_t size);
|
||||||
void sendToViewable(CNSocket *sock, void* buf, uint32_t type, size_t size);
|
void sendToViewable(CNSocket *sock, void* buf, uint32_t type, size_t size);
|
||||||
|
|
||||||
// TODO: unify this under the new Entity system
|
// TODO: unify this under the new Entity system
|
||||||
|
@ -46,49 +46,8 @@ enum class ePCRegenType {
|
|||||||
End
|
End
|
||||||
};
|
};
|
||||||
|
|
||||||
// nano powers
|
// nano power flags
|
||||||
enum {
|
enum {
|
||||||
EST_NONE = 0,
|
|
||||||
EST_DAMAGE = 1,
|
|
||||||
EST_HEAL_HP = 2,
|
|
||||||
EST_KNOCKDOWN = 3,
|
|
||||||
EST_SLEEP = 4,
|
|
||||||
EST_SNARE = 5,
|
|
||||||
EST_HEAL_STAMINA = 6,
|
|
||||||
EST_STAMINA_SELF = 7,
|
|
||||||
EST_STUN = 8,
|
|
||||||
EST_WEAPONSLOW = 9,
|
|
||||||
EST_JUMP = 10,
|
|
||||||
EST_RUN = 11,
|
|
||||||
EST_STEALTH = 12,
|
|
||||||
EST_SWIM = 13,
|
|
||||||
EST_MINIMAPENEMY = 14,
|
|
||||||
EST_MINIMAPTRESURE = 15,
|
|
||||||
EST_PHOENIX = 16,
|
|
||||||
EST_PROTECTBATTERY = 17,
|
|
||||||
EST_PROTECTINFECTION = 18,
|
|
||||||
EST_REWARDBLOB = 19,
|
|
||||||
EST_REWARDCASH = 20,
|
|
||||||
EST_BATTERYDRAIN = 21,
|
|
||||||
EST_CORRUPTIONATTACK = 22,
|
|
||||||
EST_INFECTIONDAMAGE = 23,
|
|
||||||
EST_KNOCKBACK = 24,
|
|
||||||
EST_FREEDOM = 25,
|
|
||||||
EST_PHOENIX_GROUP = 26,
|
|
||||||
EST_RECALL = 27,
|
|
||||||
EST_RECALL_GROUP = 28,
|
|
||||||
EST_RETROROCKET_SELF = 29,
|
|
||||||
EST_BLOODSUCKING = 30,
|
|
||||||
EST_BOUNDINGBALL = 31,
|
|
||||||
EST_INVULNERABLE = 32,
|
|
||||||
EST_NANOSTIMPAK = 33,
|
|
||||||
EST_RETURNHOMEHEAL = 34,
|
|
||||||
EST_BUFFHEAL = 35,
|
|
||||||
EST_EXTRABANK = 36,
|
|
||||||
EST__END = 37,
|
|
||||||
EST_CORRUPTIONATTACKWIN = 38,
|
|
||||||
EST_CORRUPTIONATTACKLOSE = 39,
|
|
||||||
|
|
||||||
ECSB_NONE = 0,
|
ECSB_NONE = 0,
|
||||||
ECSB_UP_MOVE_SPEED = 1,
|
ECSB_UP_MOVE_SPEED = 1,
|
||||||
ECSB_UP_SWIM_SPEED = 2,
|
ECSB_UP_SWIM_SPEED = 2,
|
||||||
|
Loading…
Reference in New Issue
Block a user