bool CMobController::CanMoveForward(float currentDistance) { if(PMob->m_Behaviour & BEHAVIOUR_STANDBACK && currentDistance < 20) { return false; } if(PMob->getMobMod(MOBMOD_HP_STANDBACK) > 0 && currentDistance < 20 && PMob->GetHPP() >= PMob->getMobMod(MOBMOD_HP_STANDBACK)) { // Excluding Nins, mobs should not standback if can't cast magic if (PMob->GetMJob() != JOB_NIN && PMob->SpellContainer->HasSpells() && !CanCastSpells()) { return true; } return false; } if(PMob->getMobMod(MOBMOD_SPAWN_LEASH) > 0 && distance(PMob->loc.p, PMob->m_SpawnPoint) > PMob->getMobMod(MOBMOD_SPAWN_LEASH)) { return false; } return true; }
bool CMobController::TryCastSpell() { if (!CanCastSpells()) { return false; } int chosenSpellId = -1; m_LastMagicTime = m_Tick - std::chrono::milliseconds(dsprand::GetRandomNumber(PMob->getBigMobMod(MOBMOD_MAGIC_COOL) / 2)); if (PMob->m_HasSpellScript) { // skip logic and follow script chosenSpellId = luautils::OnMonsterMagicPrepare(PMob, PTarget); if (chosenSpellId > -1) { CastSpell(chosenSpellId); return true; } } else { // find random spell if (m_firstSpell) { // mobs first spell, should be aggro spell chosenSpellId = PMob->SpellContainer->GetAggroSpell(); m_firstSpell = false; } else { chosenSpellId = PMob->SpellContainer->GetSpell(); } if (chosenSpellId > -1) { //#TODO: select target based on spell type CastSpell(chosenSpellId); return true; } } return false; }
bool CAIController::TryCastSpell() { if (!CanCastSpells()) { return false; } int chosenSpellId = -1; m_LastMagicTime = m_Tick; if (PMob->m_HasSpellScript) { // skip logic and follow script chosenSpellId = luautils::OnMonsterMagicPrepare(PMob, PTarget); if (chosenSpellId > -1) { CastSpell(chosenSpellId); return true; } } else { // find random spell if (m_firstSpell) { // mobs first spell, should be aggro spell chosenSpellId = PMob->SpellContainer->GetAggroSpell(); m_firstSpell = false; } else { chosenSpellId = PMob->SpellContainer->GetSpell(); } if (chosenSpellId > -1) { //#TODO: select target based on spell type CastSpell(chosenSpellId); return true; } } return false; }
void CMobController::DoRoamTick(time_point tick) { // If there's someone on our enmity list, go from roaming -> engaging if (PMob->PEnmityContainer->GetHighestEnmity() != nullptr && !(PMob->m_roamFlags & ROAMFLAG_IGNORE)) { Engage(PMob->PEnmityContainer->GetHighestEnmity()->targid); return; } else if (PMob->m_OwnerID.id != 0 && !(PMob->m_roamFlags & ROAMFLAG_IGNORE)) { // i'm claimed by someone and need hate towards this person PTarget = (CBattleEntity*)PMob->GetEntity(PMob->m_OwnerID.targid, TYPE_PC | TYPE_MOB | TYPE_PET); battleutils::ClaimMob(PMob, PTarget); Engage(PTarget->targid); return; } //#TODO else if (PMob->GetDespawnTime() > time_point::min() && PMob->GetDespawnTime() < m_Tick) { Despawn(); return; } if (PMob->m_roamFlags & ROAMFLAG_IGNORE) { // don't claim me if I ignore PMob->m_OwnerID.clean(); } //skip roaming if waiting if (m_Tick >= m_WaitTime) { // don't aggro a little bit after I just disengaged PMob->m_neutral = PMob->CanBeNeutral() && m_Tick <= m_NeutralTime + 10s; if (PMob->PAI->PathFind->IsFollowingPath()) { FollowRoamPath(); } else if (m_Tick >= m_LastActionTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_ROAM_COOL))) { // lets buff up or move around if (PMob->CalledForHelp()) { PMob->CallForHelp(false); } // can't rest with poison or disease if (PMob->CanRest()) { // recover 10% health if (PMob->Rest(0.1f)) { // health updated PMob->updatemask |= UPDATE_HP; } if (PMob->GetHPP() == 100) { // at max health undirty exp PMob->m_giveExp = true; } } // if I just disengaged check if I should despawn if (PMob->IsFarFromHome()) { if (PMob->CanRoamHome() && PMob->PAI->PathFind->PathTo(PMob->m_SpawnPoint)) { // walk back to spawn if too far away // limit total path to just 10 or // else we'll move straight back to spawn PMob->PAI->PathFind->LimitDistance(10.0f); FollowRoamPath(); // move back every 5 seconds m_LastActionTime = m_Tick - (std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_ROAM_COOL)) + 10s); } else if (!PMob->getMobMod(MOBMOD_NO_DESPAWN) != 0 && !map_config.mob_no_despawn) { PMob->PAI->Despawn(); return; } } else { if (PMob->getMobMod(MOBMOD_SPECIAL_SKILL) != 0 && m_Tick >= m_LastSpecialTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_SPECIAL_COOL)) && TrySpecialSkill()) { // I spawned a pet } else if (PMob->GetMJob() == JOB_SMN && CanCastSpells() && PMob->SpellContainer->HasBuffSpells() && m_Tick >= m_LastMagicTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_MAGIC_COOL))) { // summon pet CastSpell(PMob->SpellContainer->GetBuffSpell()); } else if (CanCastSpells() && dsprand::GetRandomNumber(10) < 3 && PMob->SpellContainer->HasBuffSpells()) { // cast buff CastSpell(PMob->SpellContainer->GetBuffSpell()); } else if ((PMob->m_roamFlags & ROAMFLAG_AMBUSH)) { //#TODO: #AIToScript move to scripts // stay underground PMob->HideName(true); PMob->HideModel(true); PMob->animationsub = 0; PMob->updatemask |= UPDATE_HP; } else if ((PMob->m_roamFlags & ROAMFLAG_STEALTH)) { // hidden name PMob->HideName(true); PMob->Untargetable(true); PMob->updatemask |= UPDATE_HP; } else if (PMob->m_roamFlags & ROAMFLAG_EVENT) { // allow custom event action luautils::OnMobRoamAction(PMob); m_LastActionTime = m_Tick; } else if (PMob->CanRoam() && PMob->PAI->PathFind->RoamAround(PMob->m_SpawnPoint, PMob->GetRoamDistance(), PMob->getMobMod(MOBMOD_ROAM_TURNS), PMob->m_roamFlags)) { //#TODO: #AIToScript (event probably) if (PMob->m_roamFlags & ROAMFLAG_WORM) { // move down PMob->animationsub = 1; PMob->HideName(true); // don't move around until i'm fully in the ground Wait(2s); } else { FollowRoamPath(); } } else { m_LastActionTime = m_Tick; } } } } if (m_Tick >= m_LastRoamScript + 3s) { luautils::OnMobRoam(PMob); m_LastRoamScript = m_Tick; } }
bool CAutomatonController::TrySpellcast(const CurrentManeuvers& maneuvers) { if (!PAutomaton->PMaster || m_magicCooldown == 0s || m_Tick <= m_LastMagicTime + (m_magicCooldown - std::chrono::seconds(PAutomaton->getMod(Mod::AUTO_MAGIC_DELAY))) || !CanCastSpells()) return false; switch (PAutomaton->getHead()) { case HEAD_VALOREDGE: { if (TryHeal(maneuvers)) { m_LastHealTime = m_Tick; return true; } } break; case HEAD_SHARPSHOT: { if (maneuvers.light && TryHeal(maneuvers)) // Light -> Heal { m_LastHealTime = m_Tick; return true; } if (TryEnfeeble(maneuvers)) { m_LastEnfeebleTime = m_Tick; return true; } else if (!maneuvers.light && TryHeal(maneuvers)) { m_LastHealTime = m_Tick; return true; } } break; case HEAD_HARLEQUIN: { if (maneuvers.light && TryHeal(maneuvers)) // Light -> Heal { m_LastHealTime = m_Tick; return true; } if (TryEnfeeble(maneuvers)) { m_LastEnfeebleTime = m_Tick; return true; } else if (!maneuvers.light && TryHeal(maneuvers)) { m_LastHealTime = m_Tick; return true; } } break; case HEAD_STORMWAKER: { bool lowHP = PTarget->GetHPP() <= 30 && PTarget->health.hp <= 300; if (lowHP && TryElemental(maneuvers)) // Mob low HP -> Nuke { m_LastElementalTime = m_Tick; return true; } if (maneuvers.light && TryHeal(maneuvers)) // Light -> Heal { m_LastHealTime = m_Tick; return true; } else if (!lowHP && maneuvers.ice && TryElemental(maneuvers)) // Ice -> Nuke { m_LastElementalTime = m_Tick; return true; } if (TryEnfeeble(maneuvers)) { m_LastEnfeebleTime = m_Tick; return true; } else if (!maneuvers.light && TryHeal(maneuvers)) { m_LastHealTime = m_Tick; return true; } else if (!lowHP && !maneuvers.ice && TryElemental(maneuvers)) { m_LastElementalTime = m_Tick; return true; } else if (TryEnhance()) { m_LastEnhanceTime = m_Tick; return true; } } break; case HEAD_SOULSOOTHER: { if (maneuvers.light && TryHeal(maneuvers)) // Light -> Heal { m_LastHealTime = m_Tick; return true; } if (TryStatusRemoval(maneuvers)) { m_LastStatusTime = m_Tick; return true; } else if (!maneuvers.light && TryHeal(maneuvers)) { m_LastHealTime = m_Tick; return true; } else if (TryEnhance()) { m_LastEnhanceTime = m_Tick; return true; } else if (TryEnfeeble(maneuvers)) { m_LastEnfeebleTime = m_Tick; return true; } } break; case HEAD_SPIRITREAVER: { if (maneuvers.ice && TryElemental(maneuvers)) // Ice -> Nuke { m_LastElementalTime = m_Tick; return true; } else if (maneuvers.dark && TryEnhance()) { m_LastEnhanceTime = m_Tick; return true; } else if ((maneuvers.dark || PAutomaton->GetHPP() <= 75 || PAutomaton->GetMPP() <= 75) && TryEnfeeble(maneuvers)) // Dark or self HPP/MPP <= 75 -> Enfeeble { m_LastEnfeebleTime = m_Tick; return true; } if (!maneuvers.ice && TryElemental(maneuvers)) { m_LastElementalTime = m_Tick; return true; } } } return false; }