diff --git a/src/MissionManager.cpp b/src/MissionManager.cpp index 13ea56d..c09683b 100644 --- a/src/MissionManager.cpp +++ b/src/MissionManager.cpp @@ -106,17 +106,39 @@ void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) { // failed timed missions give an iNPC_ID of 0 if (missionData->iNPC_ID == 0) { TaskData* task = MissionManager::Tasks[missionData->iTaskNum]; - // double-checking - if (task->task["m_iSTGrantTimer"] > 0) { + if (task->task["m_iSTGrantTimer"] > 0) { // its a timed mission Player* plr = PlayerManager::getPlayer(sock); - int failTaskID = task->task["m_iFOutgoingTask"]; - if (failTaskID != 0) { - MissionManager::quitTask(sock, missionData->iTaskNum, false); + /* + * Enemy killing missions + * 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; + } + } + } + } + } + + if (!mobsAreKilled) { - for (int i = 0; i < 6; i++) - if (plr->tasks[i] == missionData->iTaskNum) - plr->tasks[i] = failTaskID; - return; + int failTaskID = task->task["m_iFOutgoingTask"]; + 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; + } } } } diff --git a/src/MobManager.cpp b/src/MobManager.cpp index f73bdb2..380e4c1 100644 --- a/src/MobManager.cpp +++ b/src/MobManager.cpp @@ -443,6 +443,11 @@ void MobManager::deadStep(Mob *mob, time_t currTime) { RemovalQueue.push(mob->appearanceData.iNPC_ID); 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 @@ -457,9 +462,8 @@ void MobManager::deadStep(Mob *mob, time_t currTime) { mob->appearanceData.iHP = mob->maxHealth; mob->state = MobState::ROAMING; - // reset position + // if mob is a group leader/follower, spawn where the group is. if (mob->groupLeader != 0) { - // mob is a group leader/follower, spawn where the group is. if (Mobs.find(mob->groupLeader) != Mobs.end()) { Mob* leaderMob = Mobs[mob->groupLeader]; 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; } else { 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); @@ -540,8 +537,10 @@ void MobManager::combatStep(Mob *mob, time_t currTime) { } // 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; + } 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"]; @@ -751,16 +750,11 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) { void MobManager::step(CNServer *serv, time_t currTime) { for (auto& pair : Mobs) { - - // skip chunks without players - if (pair.second->playersInView == 0) //(!ChunkManager::inPopulatedChunks(pair.second->viewableChunks)) - continue; - if (pair.second->playersInView < 0) std::cout << "[WARN] Weird playerview value " << pair.second->playersInView << std::endl; - // skip mob movement and combat if disabled - if (!simulateMobs && pair.second->state != MobState::DEAD + // skip mob movement and combat if disabled or not in view + if ((!simulateMobs || pair.second->playersInView == 0) && pair.second->state != MobState::DEAD && pair.second->state != MobState::RETREAT) continue; @@ -1409,7 +1403,7 @@ void MobManager::followToCombat(Mob *mob) { } Mob* followerMob = Mobs[leadMob->groupMember[i]]; - if (followerMob->state == MobState::COMBAT) + if (followerMob->state != MobState::ROAMING) // only roaming mobs should transition to combat continue; followerMob->target = mob->target; @@ -1421,6 +1415,10 @@ void MobManager::followToCombat(Mob *mob) { followerMob->roamY = followerMob->appearanceData.iY; followerMob->roamZ = followerMob->appearanceData.iZ; } + + if (leadMob->state != MobState::ROAMING) + return; + leadMob->target = mob->target; leadMob->state = MobState::COMBAT; leadMob->nextMovement = getTime(); @@ -1433,6 +1431,12 @@ void MobManager::followToCombat(Mob *mob) { } 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); if (mob->skillStyle >= 0) { // corruption hit @@ -1481,7 +1485,7 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) { return; } - int random = rand() % 100 * 15000; + int random = rand() % 2000 * 1000; 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 @@ -1490,8 +1494,11 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) { int skillID = (int)mob->data["m_iActiveSkill1"]; std::vector targetData = {1, plr->iID, 0, 0, 0}; 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]); + } mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100; return; } @@ -1511,7 +1518,7 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) { mob->skillStyle = rand() % 3; 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; + mob->nextAttack = currTime + 1800; return; } @@ -1524,7 +1531,7 @@ void MobManager::useAbilities(Mob *mob, time_t currTime) { pkt.iValue2 = mob->hitY = plr->y; pkt.iValue3 = mob->hitZ = plr->z; 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; return; } @@ -1651,15 +1658,10 @@ bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, in return false; } - int damage = duration; - - if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__INVULNERABLE) - damage = 0; - respdata[i].eCT = 1; - respdata[i].iDamage = damage; + respdata[i].iDamage = duration / 10; respdata[i].iID = plr->iID; - respdata[i].iHP = plr->HP -= damage; + respdata[i].iHP = plr->HP; respdata[i].iStamina = plr->Nanos[plr->activeNano].iStamina; if (plr->iConditionBitFlag & CSB_BIT_FREEDOM) respdata[i].bProtected = 1; diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 94ad665..c57acbc 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -567,7 +567,7 @@ int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int eggId) { if (skillId == 183) { 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); } else { 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) plr->HP = 0; 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)); memset(respbuf, 0, resplen); skill->eCT = 1; @@ -651,6 +651,12 @@ void NPCManager::eggStep(CNServer* serv, time_t currTime) { plr->iConditionBitFlag &= ~CBFlag; resp.iConditionBitFlag = plr->iConditionBitFlag |= groupFlags | plr->iSelfConditionBitFlag; 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 diff --git a/src/NanoManager.cpp b/src/NanoManager.cpp index e95670a..31b6bb0 100644 --- a/src/NanoManager.cpp +++ b/src/NanoManager.cpp @@ -287,6 +287,11 @@ void NanoManager::summonNano(CNSocket *sock, int slot, bool silent) { if (slot > 2 || slot < -1) 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; 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)); } - int16_t nanoID = slot == -1 ? 0 : plr->equippedNanos[slot]; - if (nanoID > 36 || nanoID < 0) return; // sanity check plr->activeNano = nanoID; - sNano nano = plr->Nanos[nanoID]; - skillID = nano.iSkillID; + skillID = plr->Nanos[nanoID].iSkillID; // passive nano buffing if (SkillTable[skillID].drainType == 2) { @@ -494,6 +496,11 @@ int NanoManager::nanoStyle(int nanoID) { 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 NanoManager::findTargets(Player* plr, int skillID, CNPacketData* data) { std::vector tD(5); @@ -564,10 +571,13 @@ bool doDebuff(CNSocket *sock, sSkillResult_Buff *respdata, int i, int32_t target respdata[i].eCT = 4; respdata[i].iID = mob->appearanceData.iNPC_ID; - respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= bitFlag; - mob->unbuffTimes[bitFlag] = getTime() + duration * 100; - - std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl; + respdata[i].bProtected = 1; + if (mob->skillStyle < 0 && mob->state != MobState::RETREAT) { // only debuff if the enemy is not retreating and not casting corruption + mob->appearanceData.iConditionBitFlag |= bitFlag; + mob->unbuffTimes[bitFlag] = getTime() + duration * 100; + respdata[i].bProtected = 0; + } + respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag; return true; } @@ -620,15 +630,19 @@ bool doDamageNDebuff(CNSocket *sock, sSkillResult_Damage_N_Debuff *respdata, int 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].iDamage = damage; + respdata[i].iDamage = duration / 10; respdata[i].iID = mob->appearanceData.iNPC_ID; respdata[i].iHP = mob->appearanceData.iHP; - respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= bitFlag; - mob->unbuffTimes[bitFlag] = getTime() + duration * 100; - std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl; + respdata[i].bProtected = 1; + if (mob->skillStyle < 0 && mob->state != MobState::RETREAT) { // only debuff if the enemy is not retreating and not casting corruption + mob->appearanceData.iConditionBitFlag |= bitFlag; + mob->unbuffTimes[bitFlag] = getTime() + duration * 100; + respdata[i].bProtected = 0; + } + respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag; 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].iHealHP = healedAmount; - std::cout << (int)plr->iID << " was healed" << std::endl; - 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].iHP = mob->appearanceData.iHP; - std::cout << (int)mob->appearanceData.iNPC_ID << " was damaged" << std::endl; - 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->iHP = mob->appearanceData.iHP; - std::cout << (int)mob->appearanceData.iNPC_ID << " was leeched" << std::endl; - return true; }