mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 21:40:05 +00:00
Active skills, Corruption and Eruption
This commit is contained in:
parent
1371a6da77
commit
2cde3e34f6
@ -342,6 +342,9 @@ int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
|
|||||||
return 0; // no damage
|
return 0; // no damage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mob->skillStyle >= 0)
|
||||||
|
return 0; // don't hurt a mob casting corruption
|
||||||
|
|
||||||
if (mob->state == MobState::ROAMING) {
|
if (mob->state == MobState::ROAMING) {
|
||||||
assert(mob->target == nullptr);
|
assert(mob->target == nullptr);
|
||||||
mob->target = sock;
|
mob->target = sock;
|
||||||
@ -551,12 +554,15 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int distance = hypot(plr->x - mob->appearanceData.iX, plr->y - mob->appearanceData.iY);
|
int distance = hypot(plr->x - mob->appearanceData.iX, plr->y - mob->appearanceData.iY);
|
||||||
|
int mobRange = (int)mob->data["m_iAtkRange"] + (int)mob->data["m_iRadius"];
|
||||||
|
|
||||||
|
if (currTime >= mob->nextAttack)
|
||||||
|
useAbilities(mob, currTime);
|
||||||
/*
|
/*
|
||||||
* If the mob is close enough to attack, do so. If not, get closer.
|
* If the mob is close enough to attack, do so. If not, get closer.
|
||||||
* No, I'm not 100% sure this is how it's supposed to work.
|
* No, I'm not 100% sure this is how it's supposed to work.
|
||||||
*/
|
*/
|
||||||
if (distance <= (int)mob->data["m_iAtkRange"]) {
|
if (distance <= mobRange) {
|
||||||
// attack logic
|
// attack logic
|
||||||
if (mob->nextAttack == 0) {
|
if (mob->nextAttack == 0) {
|
||||||
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; //I *think* this is what this is
|
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; //I *think* this is what this is
|
||||||
@ -565,7 +571,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
||||||
npcAttackPc(mob, currTime);
|
npcAttackPc(mob, currTime);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (mob->skillStyle == -1) { // don't move while casting a skill
|
||||||
// movement logic
|
// movement logic
|
||||||
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||||
return;
|
return;
|
||||||
@ -586,7 +592,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
targetY += mob->offsetY*distance/(mob->idleRange + 1);
|
targetY += mob->offsetY*distance/(mob->idleRange + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, targetX, targetY, std::min(distance-(int)mob->data["m_iAtkRange"]+1, speed*2/5));
|
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, targetX, targetY, std::min(distance-mobRange+1, speed*2/5));
|
||||||
|
|
||||||
NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ, mob->instanceID, mob->appearanceData.iAngle);
|
NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ, mob->instanceID, mob->appearanceData.iAngle);
|
||||||
|
|
||||||
@ -1236,6 +1242,7 @@ bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MobManager::clearDebuff(Mob *mob) {
|
void MobManager::clearDebuff(Mob *mob) {
|
||||||
|
mob->skillStyle = -1;
|
||||||
mob->appearanceData.iConditionBitFlag = 0;
|
mob->appearanceData.iConditionBitFlag = 0;
|
||||||
mob->unbuffTimes.clear();
|
mob->unbuffTimes.clear();
|
||||||
|
|
||||||
@ -1458,6 +1465,190 @@ void MobManager::followToCombat(Mob *mob) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MobManager::useAbilities(Mob *mob, time_t currTime) {
|
||||||
|
if (mob->skillStyle >= 0) { // corruption hit
|
||||||
|
int skillID = (int)mob->data["m_iCorruptionType"];
|
||||||
|
int targetData[5] = {1, mob->target->plr->iID, 0, 0, 0};
|
||||||
|
dealCorruption(mob, targetData, skillID, mob->skillStyle);
|
||||||
|
mob->nextAttack = currTime + 1000;
|
||||||
|
mob->skillStyle = -3;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mob->skillStyle == -2) { // eruption hit
|
||||||
|
int skillID = (int)mob->data["m_iMegaType"];
|
||||||
|
int targetData[5] = {0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
// find the players within range of eruption
|
||||||
|
for (auto it = mob->viewableChunks->begin(); it != mob->viewableChunks->end(); it++) {
|
||||||
|
Chunk* chunk = *it;
|
||||||
|
for (CNSocket *s : chunk->players) {
|
||||||
|
Player *plr = s->plr;
|
||||||
|
|
||||||
|
if (plr->HP <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int distance = hypot(mob->hitX - plr->x, mob->hitY - plr->y);
|
||||||
|
if (distance < NanoManager::SkillTable[skillID].effectArea) {
|
||||||
|
targetData[0] += 1;
|
||||||
|
targetData[targetData[0]] = plr->iID;
|
||||||
|
if (targetData[0] > 3) // make sure not to have more than 4
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& pwr : MobPowers)
|
||||||
|
if (pwr.skillType == NanoManager::SkillTable[skillID].skillType)
|
||||||
|
pwr.handle(mob, targetData, skillID, NanoManager::SkillTable[skillID].durationTime[0], NanoManager::SkillTable[skillID].powerIntensity[0]);
|
||||||
|
mob->skillStyle = -3; // eruption cooldown
|
||||||
|
mob->nextAttack = currTime + 1000;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mob->skillStyle == -3) { // cooldown expires
|
||||||
|
mob->skillStyle = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int random = rand() % 100 * 30000;
|
||||||
|
int prob1 = (int)mob->data["m_iActiveSkill1Prob"]; // active skill probability
|
||||||
|
int prob2 = (int)mob->data["m_iCorruptionTypeProb"]; // corruption probability
|
||||||
|
int prob3 = (int)mob->data["m_iMegaTypeProb"]; // eruption probability
|
||||||
|
|
||||||
|
if (random < prob1) { // active skill hit
|
||||||
|
int skillID = (int)mob->data["m_iActiveSkill1"];
|
||||||
|
int targetData[5] = {1, mob->target->plr->iID, 0, 0, 0};
|
||||||
|
for (auto& pwr : MobPowers)
|
||||||
|
if (pwr.skillType == NanoManager::SkillTable[skillID].skillType)
|
||||||
|
pwr.handle(mob, targetData, skillID, NanoManager::SkillTable[skillID].durationTime[0], NanoManager::SkillTable[skillID].powerIntensity[0]);
|
||||||
|
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (random < prob1 + prob2) { // corruption windup
|
||||||
|
int skillID = (int)mob->data["m_iCorruptionType"];
|
||||||
|
INITSTRUCT(sP_FE2CL_NPC_SKILL_CORRUPTION_READY, pkt);
|
||||||
|
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||||
|
pkt.iSkillID = skillID;
|
||||||
|
pkt.iValue1 = mob->target->plr->x;
|
||||||
|
pkt.iValue2 = mob->target->plr->y;
|
||||||
|
pkt.iValue3 = mob->target->plr->z;
|
||||||
|
mob->skillStyle = NanoManager::nanoStyle(mob->target->plr->activeNano) - 1;
|
||||||
|
if (mob->skillStyle == -1)
|
||||||
|
mob->skillStyle = 2;
|
||||||
|
if (mob->skillStyle == -2)
|
||||||
|
mob->skillStyle = (int)mob->data["m_iNpcStyle"];
|
||||||
|
pkt.iStyle = mob->skillStyle;
|
||||||
|
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_CORRUPTION_READY, sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_READY));
|
||||||
|
mob->nextAttack = currTime + 2000;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (random < prob1 + prob2 + prob3) { // eruption windup
|
||||||
|
int skillID = (int)mob->data["m_iMegaType"];
|
||||||
|
INITSTRUCT(sP_FE2CL_NPC_SKILL_READY, pkt);
|
||||||
|
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||||
|
pkt.iSkillID = skillID;
|
||||||
|
pkt.iValue1 = mob->hitX = mob->target->plr->x;
|
||||||
|
pkt.iValue2 = mob->hitY = mob->target->plr->y;
|
||||||
|
pkt.iValue3 = mob->hitZ = mob->target->plr->z;
|
||||||
|
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_READY, sizeof(sP_FE2CL_NPC_SKILL_READY));
|
||||||
|
mob->nextAttack = currTime + 2500;
|
||||||
|
mob->skillStyle = -2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobManager::dealCorruption(Mob *mob, int targetData[], int skillID, int style) {
|
||||||
|
size_t resplen = sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_HIT) + targetData[0] * sizeof(sCAttackResult);
|
||||||
|
|
||||||
|
// validate response packet
|
||||||
|
if (!validOutVarPacket(sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_HIT), targetData[0], sizeof(sCAttackResult))) {
|
||||||
|
std::cout << "[WARN] bad sP_FE2CL_NPC_SKILL_CORRUPTION_HIT packet size" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
|
||||||
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_NPC_SKILL_CORRUPTION_HIT *resp = (sP_FE2CL_NPC_SKILL_CORRUPTION_HIT*)respbuf;
|
||||||
|
sCAttackResult *respdata = (sCAttackResult*)(respbuf+sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_HIT));
|
||||||
|
|
||||||
|
resp->iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||||
|
resp->iSkillID = skillID;
|
||||||
|
resp->iStyle = style;
|
||||||
|
resp->iValue1 = mob->target->plr->x;
|
||||||
|
resp->iValue2 = mob->target->plr->y;
|
||||||
|
resp->iValue3 = mob->target->plr->z;
|
||||||
|
resp->iTargetCnt = targetData[0];
|
||||||
|
|
||||||
|
for (int i = 0; i < targetData[0]; i++) {
|
||||||
|
CNSocket *sock = nullptr;
|
||||||
|
Player *plr = nullptr;
|
||||||
|
|
||||||
|
for (auto& pair : PlayerManager::players) {
|
||||||
|
if (pair.second->iID == targetData[i+1]) {
|
||||||
|
sock = pair.first;
|
||||||
|
plr = pair.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// player not found
|
||||||
|
if (plr == nullptr) {
|
||||||
|
std::cout << "[WARN] dealCorruption: player ID not found" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
respdata[i].eCT = 1;
|
||||||
|
respdata[i].iID = plr->iID;
|
||||||
|
respdata[i].bProtected = 0;
|
||||||
|
|
||||||
|
respdata[i].iActiveNanoSlotNum = -1;
|
||||||
|
for (int n = 0; n < 3; n++)
|
||||||
|
if (plr->activeNano == plr->equippedNanos[n])
|
||||||
|
respdata[i].iActiveNanoSlotNum = n;
|
||||||
|
respdata[i].iNanoID = plr->activeNano;
|
||||||
|
|
||||||
|
int style2 = NanoManager::nanoStyle(plr->activeNano);
|
||||||
|
if (style2 == -1 || style == style2) {
|
||||||
|
respdata[i].iHitFlag = 8; // tie
|
||||||
|
respdata[i].iDamage = 0;
|
||||||
|
respdata[i].iNanoStamina = plr->Nanos[plr->activeNano].iStamina;
|
||||||
|
} else if (style - style2 == 1 || style2 - style == 2) {
|
||||||
|
respdata[i].iHitFlag = 4; // win
|
||||||
|
respdata[i].iDamage = 60;
|
||||||
|
respdata[i].iNanoStamina = plr->Nanos[plr->activeNano].iStamina += 60;
|
||||||
|
if (plr->Nanos[plr->activeNano].iStamina > 150)
|
||||||
|
respdata[i].iNanoStamina = plr->Nanos[plr->activeNano].iStamina = 150;
|
||||||
|
} else {
|
||||||
|
respdata[i].iHitFlag = 16; // lose
|
||||||
|
respdata[i].iDamage = 60;
|
||||||
|
plr->HP -= respdata[i].iDamage;
|
||||||
|
respdata[i].iNanoStamina = plr->Nanos[plr->activeNano].iStamina -= 60;
|
||||||
|
if (plr->Nanos[plr->activeNano].iStamina < 0) {
|
||||||
|
respdata[i].iNanoStamina = plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
|
NanoManager::summonNano(sock, -1); // unsummon when stamina is 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
respdata[i].iHP = plr->HP;
|
||||||
|
respdata[i].iConditionBitFlag = plr->iConditionBitFlag;
|
||||||
|
|
||||||
|
if (plr->HP <= 0) {
|
||||||
|
mob->target = nullptr;
|
||||||
|
mob->state = MobState::RETREAT;
|
||||||
|
if (!aggroCheck(mob, getTime()))
|
||||||
|
clearDebuff(mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_NPC_SKILL_CORRUPTION_HIT, resplen);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma region Mob Powers
|
#pragma region Mob Powers
|
||||||
namespace MobManager {
|
namespace MobManager {
|
||||||
bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) {
|
bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, int32_t targetID, int32_t bitFlag, int16_t timeBuffID, int16_t duration, int16_t amount) {
|
||||||
@ -1478,7 +1669,7 @@ bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, in
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int damage = duration / 1000;
|
int damage = duration;
|
||||||
|
|
||||||
respdata[i].eCT = 1;
|
respdata[i].eCT = 1;
|
||||||
respdata[i].iDamage = damage;
|
respdata[i].iDamage = damage;
|
||||||
@ -1488,13 +1679,29 @@ bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, in
|
|||||||
if (plr->iConditionBitFlag & CSB_BIT_FREEDOM)
|
if (plr->iConditionBitFlag & CSB_BIT_FREEDOM)
|
||||||
respdata[i].bProtected = 1;
|
respdata[i].bProtected = 1;
|
||||||
else {
|
else {
|
||||||
|
if (!(plr->iConditionBitFlag & bitFlag)) {
|
||||||
|
INITSTRUCT(sP_FE2CL_PC_BUFF_UPDATE, pkt);
|
||||||
|
pkt.eCSTB = timeBuffID; // eCharStatusTimeBuffID
|
||||||
|
pkt.eTBU = 1; // eTimeBuffUpdate
|
||||||
|
pkt.eTBT = 2;
|
||||||
|
pkt.iConditionBitFlag = plr->iConditionBitFlag |= bitFlag;
|
||||||
|
sock->sendPacket((void*)&pkt, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
|
||||||
|
}
|
||||||
|
|
||||||
respdata[i].bProtected = 0;
|
respdata[i].bProtected = 0;
|
||||||
plr->iConditionBitFlag |= bitFlag;
|
|
||||||
std::pair<CNSocket*, int32_t> key = std::make_pair(sock, bitFlag);
|
std::pair<CNSocket*, int32_t> key = std::make_pair(sock, bitFlag);
|
||||||
time_t until = getTime() + (time_t)duration;
|
time_t until = getTime() + (time_t)duration * 100;
|
||||||
NPCManager::EggBuffs[key] = until;
|
NPCManager::EggBuffs[key] = until;
|
||||||
}
|
}
|
||||||
respdata[i].iConditionBitFlag = plr->iConditionBitFlag;
|
respdata[i].iConditionBitFlag = plr->iConditionBitFlag;
|
||||||
|
|
||||||
|
if (plr->HP <= 0) {
|
||||||
|
mob->target = nullptr;
|
||||||
|
mob->state = MobState::RETREAT;
|
||||||
|
if (!aggroCheck(mob, getTime()))
|
||||||
|
clearDebuff(mob);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1535,6 +1742,13 @@ bool doDamage(Mob *mob, sSkillResult_Damage *respdata, int i, int32_t targetID,
|
|||||||
respdata[i].iID = plr->iID;
|
respdata[i].iID = plr->iID;
|
||||||
respdata[i].iHP = plr->HP -= damage;
|
respdata[i].iHP = plr->HP -= damage;
|
||||||
|
|
||||||
|
if (plr->HP <= 0) {
|
||||||
|
mob->target = nullptr;
|
||||||
|
mob->state = MobState::RETREAT;
|
||||||
|
if (!aggroCheck(mob, getTime()))
|
||||||
|
clearDebuff(mob);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1579,6 +1793,13 @@ bool doLeech(Mob *mob, sSkillResult_Heal_HP *healdata, int i, int32_t targetID,
|
|||||||
damagedata->iID = plr->iID;
|
damagedata->iID = plr->iID;
|
||||||
damagedata->iHP = plr->HP -= damage;
|
damagedata->iHP = plr->HP -= damage;
|
||||||
|
|
||||||
|
if (plr->HP <= 0) {
|
||||||
|
mob->target = nullptr;
|
||||||
|
mob->state = MobState::RETREAT;
|
||||||
|
if (!aggroCheck(mob, getTime()))
|
||||||
|
clearDebuff(mob);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1594,7 +1815,7 @@ bool doBatteryDrain(Mob *mob, sSkillResult_BatteryDrain *respdata, int i, int32_
|
|||||||
|
|
||||||
// player not found
|
// player not found
|
||||||
if (plr == nullptr) {
|
if (plr == nullptr) {
|
||||||
std::cout << "[WARN] doDamage: player ID not found" << std::endl;
|
std::cout << "[WARN] doBatteryDrain: player ID not found" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ struct Mob : public BaseNPC {
|
|||||||
CNSocket *target = nullptr;
|
CNSocket *target = nullptr;
|
||||||
time_t nextAttack = 0;
|
time_t nextAttack = 0;
|
||||||
time_t lastDrainTime = 0;
|
time_t lastDrainTime = 0;
|
||||||
int skillStyle = -1; // -1 for nothing, 0-2 for corruption, -2 for ability windup, -3 for eruption
|
int skillStyle = -1; // -1 for nothing, 0-2 for corruption, -2 for eruption
|
||||||
int hitX, hitY, hitZ; // for use in ability targeting
|
int hitX, hitY, hitZ; // for use in ability targeting
|
||||||
|
|
||||||
// drop
|
// drop
|
||||||
@ -148,6 +148,7 @@ namespace MobManager {
|
|||||||
extern std::map<int32_t, MobDrop> MobDrops;
|
extern std::map<int32_t, MobDrop> MobDrops;
|
||||||
extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets;
|
extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets;
|
||||||
extern bool simulateMobs;
|
extern bool simulateMobs;
|
||||||
|
extern std::vector<MobPower> MobPowers;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void step(CNServer*, time_t);
|
void step(CNServer*, time_t);
|
||||||
@ -187,5 +188,6 @@ namespace MobManager {
|
|||||||
int8_t addBullet(Player* plr, bool isGrenade);
|
int8_t addBullet(Player* plr, bool isGrenade);
|
||||||
|
|
||||||
void followToCombat(Mob *mob);
|
void followToCombat(Mob *mob);
|
||||||
|
void useAbilities(Mob *mob, time_t currTime);
|
||||||
|
void dealCorruption(Mob *mob, int *targetData, int skillID, int style);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user