Various bugfixes

- Eruption is now blocked by stun and sleep.
- Corruption should block all nano abilities.
- Buffs time out for other players
- Timed mission bugfixes (AGAIN)
- Corruption and Eruptions fire quicker.
- Heal egg ids fix
- No power nanos no longer break the system.
- Mobs should no longer restun.
- Mob ability chance calculation adjustments.
- Duration of the power's debuff is sent as iDamage instead of 0, this removes the ugly "Block" that shows up on successful hits.
- Group mob respawning bugfixes
- a bit of a cleanup
This commit is contained in:
JadeShrineMaiden 2020-12-12 22:22:22 +00:00 committed by GitHub
parent 1474ff10ac
commit b947ff65cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 59 deletions

View File

@ -106,17 +106,39 @@ void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) {
// failed timed missions give an iNPC_ID of 0 // failed timed missions give an iNPC_ID of 0
if (missionData->iNPC_ID == 0) { if (missionData->iNPC_ID == 0) {
TaskData* task = MissionManager::Tasks[missionData->iTaskNum]; TaskData* task = MissionManager::Tasks[missionData->iTaskNum];
// double-checking if (task->task["m_iSTGrantTimer"] > 0) { // its a timed mission
if (task->task["m_iSTGrantTimer"] > 0) {
Player* plr = PlayerManager::getPlayer(sock); Player* plr = PlayerManager::getPlayer(sock);
int failTaskID = task->task["m_iFOutgoingTask"]; /*
if (failTaskID != 0) { * Enemy killing missions
MissionManager::quitTask(sock, missionData->iTaskNum, false); * this is gross and should be cleaned up later
* once we comb over mission logic more throughly
*/
bool mobsAreKilled = false;
if (task->task["m_iHTaskType"] == 5) {
mobsAreKilled = true;
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
if (plr->tasks[i] == missionData->iTaskNum) {
for (int j = 0; j < 3; j++) {
if (plr->RemainingNPCCount[i][j] > 0) {
mobsAreKilled = false;
break;
}
}
}
}
}
for (int i = 0; i < 6; i++) if (!mobsAreKilled) {
if (plr->tasks[i] == missionData->iTaskNum)
plr->tasks[i] = failTaskID; int failTaskID = task->task["m_iFOutgoingTask"];
return; if (failTaskID != 0) {
MissionManager::quitTask(sock, missionData->iTaskNum, false);
for (int i = 0; i < 6; i++)
if (plr->tasks[i] == missionData->iTaskNum)
plr->tasks[i] = failTaskID;
return;
}
} }
} }
} }

View File

@ -443,6 +443,11 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
RemovalQueue.push(mob->appearanceData.iNPC_ID); RemovalQueue.push(mob->appearanceData.iNPC_ID);
return; return;
} }
// pre-set spawn coordinates if not marked for removal
mob->appearanceData.iX = mob->spawnX;
mob->appearanceData.iY = mob->spawnY;
mob->appearanceData.iZ = mob->spawnZ;
} }
// to guide their groupmates, group leaders still need to move despite being dead // to guide their groupmates, group leaders still need to move despite being dead
@ -457,9 +462,8 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
mob->appearanceData.iHP = mob->maxHealth; mob->appearanceData.iHP = mob->maxHealth;
mob->state = MobState::ROAMING; mob->state = MobState::ROAMING;
// reset position // if mob is a group leader/follower, spawn where the group is.
if (mob->groupLeader != 0) { if (mob->groupLeader != 0) {
// mob is a group leader/follower, spawn where the group is.
if (Mobs.find(mob->groupLeader) != Mobs.end()) { if (Mobs.find(mob->groupLeader) != Mobs.end()) {
Mob* leaderMob = Mobs[mob->groupLeader]; Mob* leaderMob = Mobs[mob->groupLeader];
mob->appearanceData.iX = leaderMob->appearanceData.iX + mob->offsetX; mob->appearanceData.iX = leaderMob->appearanceData.iX + mob->offsetX;
@ -467,14 +471,7 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
mob->appearanceData.iZ = leaderMob->appearanceData.iZ; mob->appearanceData.iZ = leaderMob->appearanceData.iZ;
} else { } else {
std::cout << "[WARN] deadStep: mob cannot find it's leader!" << std::endl; std::cout << "[WARN] deadStep: mob cannot find it's leader!" << std::endl;
mob->appearanceData.iX = mob->spawnX;
mob->appearanceData.iY = mob->spawnY;
mob->appearanceData.iZ = mob->spawnZ;
} }
} else {
mob->appearanceData.iX = mob->spawnX;
mob->appearanceData.iY = mob->spawnY;
mob->appearanceData.iZ = mob->spawnZ;
} }
INITSTRUCT(sP_FE2CL_NPC_NEW, pkt); INITSTRUCT(sP_FE2CL_NPC_NEW, pkt);
@ -540,8 +537,10 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
} }
// skip attack if stunned or asleep // skip attack if stunned or asleep
if (mob->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ)) if (mob->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ)) {
mob->skillStyle = -1; // in this case we also reset the any outlying abilities the mob might be winding up.
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"]; int mobRange = (int)mob->data["m_iAtkRange"] + (int)mob->data["m_iRadius"];
@ -751,16 +750,11 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
void MobManager::step(CNServer *serv, time_t currTime) { void MobManager::step(CNServer *serv, time_t currTime) {
for (auto& pair : Mobs) { for (auto& pair : Mobs) {
// skip chunks without players
if (pair.second->playersInView == 0) //(!ChunkManager::inPopulatedChunks(pair.second->viewableChunks))
continue;
if (pair.second->playersInView < 0) if (pair.second->playersInView < 0)
std::cout << "[WARN] Weird playerview value " << pair.second->playersInView << std::endl; std::cout << "[WARN] Weird playerview value " << pair.second->playersInView << std::endl;
// skip mob movement and combat if disabled // skip mob movement and combat if disabled or not in view
if (!simulateMobs && pair.second->state != MobState::DEAD if ((!simulateMobs || pair.second->playersInView == 0) && pair.second->state != MobState::DEAD
&& pair.second->state != MobState::RETREAT) && pair.second->state != MobState::RETREAT)
continue; continue;
@ -1409,7 +1403,7 @@ void MobManager::followToCombat(Mob *mob) {
} }
Mob* followerMob = Mobs[leadMob->groupMember[i]]; Mob* followerMob = Mobs[leadMob->groupMember[i]];
if (followerMob->state == MobState::COMBAT) if (followerMob->state != MobState::ROAMING) // only roaming mobs should transition to combat
continue; continue;
followerMob->target = mob->target; followerMob->target = mob->target;
@ -1421,6 +1415,10 @@ void MobManager::followToCombat(Mob *mob) {
followerMob->roamY = followerMob->appearanceData.iY; followerMob->roamY = followerMob->appearanceData.iY;
followerMob->roamZ = followerMob->appearanceData.iZ; followerMob->roamZ = followerMob->appearanceData.iZ;
} }
if (leadMob->state != MobState::ROAMING)
return;
leadMob->target = mob->target; leadMob->target = mob->target;
leadMob->state = MobState::COMBAT; leadMob->state = MobState::COMBAT;
leadMob->nextMovement = getTime(); leadMob->nextMovement = getTime();
@ -1433,6 +1431,12 @@ void MobManager::followToCombat(Mob *mob) {
} }
void MobManager::useAbilities(Mob *mob, time_t currTime) { void MobManager::useAbilities(Mob *mob, time_t currTime) {
/*
* targetData approach
* first integer is the count
* second to fifth integers are IDs, these can be either player iID or mob's iID
* whether the skill targets players or mobs is determined by the skill packet being fired
*/
Player *plr = PlayerManager::getPlayer(mob->target); Player *plr = PlayerManager::getPlayer(mob->target);
if (mob->skillStyle >= 0) { // corruption hit if (mob->skillStyle >= 0) { // corruption hit
@ -1481,7 +1485,7 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) {
return; return;
} }
int random = rand() % 100 * 15000; int random = rand() % 2000 * 1000;
int prob1 = (int)mob->data["m_iActiveSkill1Prob"]; // active skill probability int prob1 = (int)mob->data["m_iActiveSkill1Prob"]; // active skill probability
int prob2 = (int)mob->data["m_iCorruptionTypeProb"]; // corruption probability int prob2 = (int)mob->data["m_iCorruptionTypeProb"]; // corruption probability
int prob3 = (int)mob->data["m_iMegaTypeProb"]; // eruption probability int prob3 = (int)mob->data["m_iMegaTypeProb"]; // eruption probability
@ -1490,8 +1494,11 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) {
int skillID = (int)mob->data["m_iActiveSkill1"]; int skillID = (int)mob->data["m_iActiveSkill1"];
std::vector<int> targetData = {1, plr->iID, 0, 0, 0}; std::vector<int> targetData = {1, plr->iID, 0, 0, 0};
for (auto& pwr : MobPowers) for (auto& pwr : MobPowers)
if (pwr.skillType == NanoManager::SkillTable[skillID].skillType) if (pwr.skillType == NanoManager::SkillTable[skillID].skillType) {
if (pwr.bitFlag != 0 && (plr->iConditionBitFlag & pwr.bitFlag))
return; // prevent debuffing a player twice
pwr.handle(mob, targetData, skillID, NanoManager::SkillTable[skillID].durationTime[0], NanoManager::SkillTable[skillID].powerIntensity[0]); pwr.handle(mob, targetData, skillID, NanoManager::SkillTable[skillID].durationTime[0], NanoManager::SkillTable[skillID].powerIntensity[0]);
}
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100; mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
return; return;
} }
@ -1511,7 +1518,7 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) {
mob->skillStyle = rand() % 3; mob->skillStyle = rand() % 3;
pkt.iStyle = mob->skillStyle; pkt.iStyle = mob->skillStyle;
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_CORRUPTION_READY, sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_READY)); NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_CORRUPTION_READY, sizeof(sP_FE2CL_NPC_SKILL_CORRUPTION_READY));
mob->nextAttack = currTime + 2000; mob->nextAttack = currTime + 1800;
return; return;
} }
@ -1524,7 +1531,7 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) {
pkt.iValue2 = mob->hitY = plr->y; pkt.iValue2 = mob->hitY = plr->y;
pkt.iValue3 = mob->hitZ = plr->z; pkt.iValue3 = mob->hitZ = plr->z;
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_READY, sizeof(sP_FE2CL_NPC_SKILL_READY)); NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_SKILL_READY, sizeof(sP_FE2CL_NPC_SKILL_READY));
mob->nextAttack = currTime + 2500; mob->nextAttack = currTime + 1800;
mob->skillStyle = -2; mob->skillStyle = -2;
return; return;
} }
@ -1651,15 +1658,10 @@ bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, in
return false; return false;
} }
int damage = duration;
if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE)
damage = 0;
respdata[i].eCT = 1; respdata[i].eCT = 1;
respdata[i].iDamage = damage; respdata[i].iDamage = duration / 10;
respdata[i].iID = plr->iID; respdata[i].iID = plr->iID;
respdata[i].iHP = plr->HP -= damage; respdata[i].iHP = plr->HP;
respdata[i].iStamina = plr->Nanos[plr->activeNano].iStamina; respdata[i].iStamina = plr->Nanos[plr->activeNano].iStamina;
if (plr->iConditionBitFlag & CSB_BIT_FREEDOM) if (plr->iConditionBitFlag & CSB_BIT_FREEDOM)
respdata[i].bProtected = 1; respdata[i].bProtected = 1;

View File

@ -567,7 +567,7 @@ int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int eggId) {
if (skillId == 183) { if (skillId == 183) {
resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Damage); resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Damage);
} else if (skillId == 147) { } else if (skillId == 150) {
resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Heal_HP); resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Heal_HP);
} else { } else {
resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Buff); resplen = sizeof(sP_FE2CL_NPC_SKILL_HIT) + sizeof(sSkillResult_Buff);
@ -588,7 +588,7 @@ int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int eggId) {
if (plr->HP < 0) if (plr->HP < 0)
plr->HP = 0; plr->HP = 0;
skill->iHP = plr->HP; skill->iHP = plr->HP;
} else if (skillId == 147) { // heal egg } else if (skillId == 150) { // heal egg
sSkillResult_Heal_HP* skill = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT)); sSkillResult_Heal_HP* skill = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_NPC_SKILL_HIT));
memset(respbuf, 0, resplen); memset(respbuf, 0, resplen);
skill->eCT = 1; skill->eCT = 1;
@ -651,6 +651,12 @@ void NPCManager::eggStep(CNServer* serv, time_t currTime) {
plr->iConditionBitFlag &= ~CBFlag; plr->iConditionBitFlag &= ~CBFlag;
resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag;
sock->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE)); sock->sendPacket((void*)&resp, P_FE2CL_PC_BUFF_UPDATE, sizeof(sP_FE2CL_PC_BUFF_UPDATE));
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, resp2); // send a buff timeout to other players
resp2.eCT = 1;
resp2.iID = plr->iID;
resp2.iConditionBitFlag = plr->iConditionBitFlag;
PlayerManager::sendToViewable(sock, (void*)&resp2, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
} }
} }
// remove buff from the map // remove buff from the map

View File

@ -287,6 +287,11 @@ void NanoManager::summonNano(CNSocket *sock, int slot, bool silent) {
if (slot > 2 || slot < -1) if (slot > 2 || slot < -1)
return; // sanity check return; // sanity check
int16_t nanoID = slot == -1 ? 0 : plr->equippedNanos[slot];
if (slot != -1 && plr->Nanos[nanoID].iSkillID == 0)
return; // prevent powerless nanos from summoning
plr->nanoDrainRate = 0; plr->nanoDrainRate = 0;
int16_t skillID = plr->Nanos[plr->activeNano].iSkillID; int16_t skillID = plr->Nanos[plr->activeNano].iSkillID;
@ -299,14 +304,11 @@ void NanoManager::summonNano(CNSocket *sock, int slot, bool silent) {
nanoUnbuff(sock, targetData, pwr.bitFlag, pwr.timeBuffID, 0,(SkillTable[skillID].targetType == 3)); nanoUnbuff(sock, targetData, pwr.bitFlag, pwr.timeBuffID, 0,(SkillTable[skillID].targetType == 3));
} }
int16_t nanoID = slot == -1 ? 0 : plr->equippedNanos[slot];
if (nanoID > 36 || nanoID < 0) if (nanoID > 36 || nanoID < 0)
return; // sanity check return; // sanity check
plr->activeNano = nanoID; plr->activeNano = nanoID;
sNano nano = plr->Nanos[nanoID]; skillID = plr->Nanos[nanoID].iSkillID;
skillID = nano.iSkillID;
// passive nano buffing // passive nano buffing
if (SkillTable[skillID].drainType == 2) { if (SkillTable[skillID].drainType == 2) {
@ -494,6 +496,11 @@ int NanoManager::nanoStyle(int nanoID) {
return NanoTable[nanoID].style; return NanoTable[nanoID].style;
} }
/*
* targetData approach
* first integer is the count
* second to fifth integers are IDs, these can be either player iID or mob's iID
*/
std::vector<int> NanoManager::findTargets(Player* plr, int skillID, CNPacketData* data) { std::vector<int> NanoManager::findTargets(Player* plr, int skillID, CNPacketData* data) {
std::vector<int> tD(5); std::vector<int> tD(5);
@ -564,10 +571,13 @@ bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t target
respdata[i].eCT = 4; respdata[i].eCT = 4;
respdata[i].iID = mob->appearanceData.iNPC_ID; respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= bitFlag; respdata[i].bProtected = 1;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100; if (mob->skillStyle < 0 && mob->state != MobState::RETREAT) { // only debuff if the enemy is not retreating and not casting corruption
mob->appearanceData.iConditionBitFlag |= bitFlag;
std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl; mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
respdata[i].bProtected = 0;
}
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
return true; return true;
} }
@ -620,15 +630,19 @@ bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int
Mob* mob = MobManager::Mobs[targetID]; Mob* mob = MobManager::Mobs[targetID];
int damage = MobManager::hitMob(sock, mob, 0); // using amount for something else MobManager::hitMob(sock, mob, 0); // just to gain aggro
respdata[i].eCT = 4; respdata[i].eCT = 4;
respdata[i].iDamage = damage; respdata[i].iDamage = duration / 10;
respdata[i].iID = mob->appearanceData.iNPC_ID; respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP; respdata[i].iHP = mob->appearanceData.iHP;
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= bitFlag; respdata[i].bProtected = 1;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100; if (mob->skillStyle < 0 && mob->state != MobState::RETREAT) { // only debuff if the enemy is not retreating and not casting corruption
std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl; mob->appearanceData.iConditionBitFlag |= bitFlag;
mob->unbuffTimes[bitFlag] = getTime() + duration * 100;
respdata[i].bProtected = 0;
}
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
return true; return true;
} }
@ -665,8 +679,6 @@ bool doHeal(CNSocket *sock, sSkillResult_Heal_HP *respdata, int i, int32_t targe
respdata[i].iHP = plr->HP; respdata[i].iHP = plr->HP;
respdata[i].iHealHP = healedAmount; respdata[i].iHealHP = healedAmount;
std::cout << (int)plr->iID << " was healed" << std::endl;
return true; return true;
} }
@ -687,8 +699,6 @@ bool doDamage(CNSocket *sock, sSkillResult_Damage *respdata, int i, int32_t targ
respdata[i].iID = mob->appearanceData.iNPC_ID; respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP; respdata[i].iHP = mob->appearanceData.iHP;
std::cout << (int)mob->appearanceData.iNPC_ID << " was damaged" << std::endl;
return true; return true;
} }
@ -737,8 +747,6 @@ bool doLeech(CNSocket *sock, sSkillResult_Heal_HP *healdata, int i, int32_t targ
damagedata->iID = mob->appearanceData.iNPC_ID; damagedata->iID = mob->appearanceData.iNPC_ID;
damagedata->iHP = mob->appearanceData.iHP; damagedata->iHP = mob->appearanceData.iHP;
std::cout << (int)mob->appearanceData.iNPC_ID << " was leeched" << std::endl;
return true; return true;
} }