bool CAutomatonController::TryEnhance() { if (!PAutomaton->PMaster || m_enhanceCooldown == 0s || m_Tick <= m_LastEnhanceTime + m_enhanceCooldown) return false; if (PAutomaton->getHead() == HEAD_SPIRITREAVER) return Cast(PAutomaton->targid, SpellID::Dread_Spikes); EnmityList_t* enmityList; auto PMob = dynamic_cast<CMobEntity*>(PTarget); if (PMob) enmityList = PMob->PEnmityContainer->GetEnmityList(); uint16 highestEnmity = 0; CBattleEntity* PRegenTarget = nullptr; CBattleEntity* PProtectTarget = nullptr; CBattleEntity* PShellTarget = nullptr; CBattleEntity* PHasteTarget = nullptr; CBattleEntity* PStoneSkinTarget = nullptr; CBattleEntity* PPhalanxTarget = nullptr; bool protect = false; uint8 protectcount = 0; bool shell = false; uint8 shellcount = 0; bool haste = false; bool stoneskin = false; bool phalanx = false; bool isEngaged = false; if (distance(PAutomaton->loc.p, PAutomaton->PMaster->loc.p) < 20) { if (PMob) { auto enmity_obj = enmityList->find(PAutomaton->PMaster->id); if (enmity_obj != enmityList->end()) { isEngaged = true; if (highestEnmity < enmity_obj->second.CE + enmity_obj->second.VE) { highestEnmity = enmity_obj->second.CE + enmity_obj->second.VE; PRegenTarget = PAutomaton->PMaster; } } } else { isEngaged = true; // Assume everyone is engaged if the target isn't a mob } PAutomaton->PMaster->StatusEffectContainer->ForEachEffect([&protect, &protectcount, &shell, &shellcount, &haste, &stoneskin, &phalanx](CStatusEffect* PStatus) { if (PStatus->GetDuration() > 0) { if (PStatus->GetStatusID() == EFFECT_PROTECT) { protect = true; ++protectcount; } if (PStatus->GetStatusID() == EFFECT_SHELL) { shell = true; ++shellcount; } if (PStatus->GetStatusID() == EFFECT_HASTE || PStatus->GetStatusID() == EFFECT_HASTE_II) haste = true; if (PStatus->GetStatusID() == EFFECT_STONESKIN) stoneskin = true; if (PStatus->GetStatusID() == EFFECT_PHALANX) phalanx = true; } }); if (isEngaged) { if (!protect) PProtectTarget = PAutomaton->PMaster; if (!shell) PShellTarget = PAutomaton->PMaster; if (!haste) PHasteTarget = PAutomaton->PMaster; if (!stoneskin) PStoneSkinTarget = PAutomaton->PMaster; if (!phalanx) PPhalanxTarget = PAutomaton->PMaster; } } protect = false; shell = false; haste = false; stoneskin = false; phalanx = false; if (PMob) { auto enmity_obj = enmityList->find(PAutomaton->id); if (enmity_obj != enmityList->end() && highestEnmity < enmity_obj->second.CE + enmity_obj->second.VE) { highestEnmity = enmity_obj->second.CE + enmity_obj->second.VE; PRegenTarget = PAutomaton; } } PAutomaton->StatusEffectContainer->ForEachEffect([&protect, &shell, &haste, &stoneskin, &phalanx](CStatusEffect* PStatus) { if (PStatus->GetDuration() > 0) { if (PStatus->GetStatusID() == EFFECT_PROTECT) protect = true; if (PStatus->GetStatusID() == EFFECT_SHELL) shell = true; if (PStatus->GetStatusID() == EFFECT_HASTE || PStatus->GetStatusID() == EFFECT_HASTE_II) haste = true; } }); if (!PProtectTarget && !protect) PProtectTarget = PAutomaton; if (!PShellTarget && !shell) PShellTarget = PAutomaton; if (!PHasteTarget && !haste) PHasteTarget = PAutomaton; uint8 members = 0; // Unknown whether it only applies buffs to other members if they have hate or if the Soulsoother head is needed if (PAutomaton->PMaster->PParty) { members = PAutomaton->PMaster->PParty->members.size(); for (uint8 i = 0; i < members; ++i) { CBattleEntity* member = PAutomaton->PMaster->PParty->members.at(i); if (member->id != PAutomaton->PMaster->id && distance(PAutomaton->loc.p, member->loc.p) < 20) { protect = false; shell = false; haste = false; isEngaged = false; if (PMob) { auto enmity_obj = enmityList->find(member->id); if (enmity_obj != enmityList->end()) { isEngaged = true; if (highestEnmity < enmity_obj->second.CE + enmity_obj->second.VE) { highestEnmity = enmity_obj->second.CE + enmity_obj->second.VE; PRegenTarget = member; } } } else { isEngaged = true; // Assume everyone is engaged if the target isn't a mob } member->StatusEffectContainer->ForEachEffect([&protect, &protectcount, &shell, &shellcount, &haste](CStatusEffect* PStatus) { if (PStatus->GetDuration() > 0) { if (PStatus->GetStatusID() == EFFECT_PROTECT) { protect = true; ++protectcount; } if (PStatus->GetStatusID() == EFFECT_SHELL) { shell = true; ++shellcount; } if (PStatus->GetStatusID() == EFFECT_HASTE || PStatus->GetStatusID() == EFFECT_HASTE_II) haste = true; } }); if (isEngaged) { if (!PProtectTarget && !protect) PProtectTarget = member; if (!PShellTarget && !shell) PShellTarget = member; if (!PHasteTarget && !haste) PHasteTarget = member; } } } } // No info on how this spell worked if((members - protectcount) >= 4) Cast(PAutomaton->targid, SpellID::Protectra_V); // No info on how this spell worked if ((members - shellcount) >= 4) Cast(PAutomaton->targid, SpellID::Shellra_V); if (PRegenTarget && (PTarget->GetMLevel() + 5) >= PAutomaton->GetMLevel() && !(PRegenTarget->StatusEffectContainer->HasStatusEffect(EFFECT_REGEN) || PRegenTarget->StatusEffectContainer->HasStatusEffect(EFFECT_REGEN_II))) if (Cast(PRegenTarget->targid, SpellID::Regen_III) || Cast(PRegenTarget->targid, SpellID::Regen_II) || Cast(PRegenTarget->targid, SpellID::Regen)) return true; if (PProtectTarget) if (Cast(PProtectTarget->targid, SpellID::Protect_V) || Cast(PProtectTarget->targid, SpellID::Protect_IV) || Cast(PProtectTarget->targid, SpellID::Protect_III) || Cast(PProtectTarget->targid, SpellID::Protect_II) || Cast(PProtectTarget->targid, SpellID::Protect)) return true; if (PShellTarget) if (Cast(PShellTarget->targid, SpellID::Shell_V) || Cast(PShellTarget->targid, SpellID::Shell_IV) || Cast(PShellTarget->targid, SpellID::Shell_III) || Cast(PShellTarget->targid, SpellID::Shell_II) || Cast(PShellTarget->targid, SpellID::Shell)) return true; if (PHasteTarget) if (Cast(PHasteTarget->targid, SpellID::Haste_II) || Cast(PHasteTarget->targid, SpellID::Haste)) return true; if (PStoneSkinTarget) if (Cast(PStoneSkinTarget->targid, SpellID::Stoneskin)) return true; if (PPhalanxTarget) if (Cast(PPhalanxTarget->targid, SpellID::Phalanx)) return true; return false; }
bool CAutomatonController::TryHeal(const CurrentManeuvers& maneuvers) { if (!PAutomaton->PMaster || m_healCooldown == 0s || m_Tick <= m_LastHealTime + (m_healCooldown - std::chrono::seconds(PAutomaton->getMod(Mod::AUTO_HEALING_DELAY)))) return false; float threshold = 0; switch (maneuvers.light) // Light -> Higher healing threshold { case 1: threshold = 40; break; case 2: threshold = 50; break; case 3: threshold = 75; break; default: threshold = 30; break; } threshold = dsp_cap(threshold + PAutomaton->getMod(Mod::AUTO_HEALING_THRESHOLD), 30, 90); CBattleEntity* PCastTarget = nullptr; bool haveHate = false; EnmityList_t* enmityList; auto PMob = dynamic_cast<CMobEntity*>(PTarget); if (PMob) { enmityList = PMob->PEnmityContainer->GetEnmityList(); auto masterEnmity_obj = enmityList->find(PAutomaton->PMaster->id); auto selfEnmity_obj = enmityList->find(PAutomaton->id); if (masterEnmity_obj == enmityList->end()) haveHate = true; else if (selfEnmity_obj == enmityList->end()) haveHate = false; else { uint16 selfEnmity = selfEnmity_obj->second.CE + selfEnmity_obj->second.VE; uint16 masterEnmity = masterEnmity_obj->second.CE + masterEnmity_obj->second.VE; haveHate = selfEnmity > masterEnmity ? true : false; } } // Prioritize hate if (haveHate) { if (PAutomaton->GetHPP() <= 50) // Automaton only heals itself when <= 50% PCastTarget = PAutomaton; else if (PAutomaton->PMaster->GetHPP() <= threshold && distance(PAutomaton->loc.p, PAutomaton->PMaster->loc.p) < 20) PCastTarget = PAutomaton->PMaster; } else { if (PAutomaton->PMaster->GetHPP() <= threshold) PCastTarget = PAutomaton->PMaster; else if (PAutomaton->GetHPP() <= 50) // Automaton only heals itself when <= 50% PCastTarget = PAutomaton; } if (maneuvers.light && !PCastTarget && PAutomaton->getHead() == HEAD_SOULSOOTHER && PAutomaton->PMaster->PParty) // Light + Soulsoother head -> Heal party { if (PMob) { uint16 highestEnmity = 0; for (uint8 i = 0; i < PAutomaton->PMaster->PParty->members.size(); ++i) { CBattleEntity* member = PAutomaton->PMaster->PParty->members.at(i); if (member->id != PAutomaton->PMaster->id) { auto enmity_obj = enmityList->find(member->id); if (enmity_obj != enmityList->end() && highestEnmity < enmity_obj->second.CE + enmity_obj->second.VE && member->GetHPP() <= threshold && distance(PAutomaton->loc.p, PAutomaton->PMaster->loc.p) < 20) { highestEnmity = enmity_obj->second.CE + enmity_obj->second.VE; PCastTarget = member; } } } } else { for (uint8 i = 0; i < PAutomaton->PMaster->PParty->members.size(); ++i) { CBattleEntity* member = PAutomaton->PMaster->PParty->members.at(i); if (member->id != PAutomaton->PMaster->id && distance(PAutomaton->loc.p, PAutomaton->PMaster->loc.p) < 20) { if (member->GetHPP() <= threshold) { PCastTarget = member; break; } } } } } // This might be wrong if (PCastTarget) { float missinghp = PCastTarget->GetMaxHP() - PCastTarget->health.hp; if (missinghp > 850 && Cast(PCastTarget->targid, SpellID::Cure_VI )) return true; else if (missinghp > 600 && Cast(PCastTarget->targid, SpellID::Cure_V)) return true; else if (missinghp > 350 && Cast(PCastTarget->targid, SpellID::Cure_IV)) return true; else if (missinghp > 190 && Cast(PCastTarget->targid, SpellID::Cure_III)) return true; else if (missinghp > 120 && Cast(PCastTarget->targid, SpellID::Cure_II)) return true; else if (Cast(PCastTarget->targid, SpellID::Cure)) return true; } return false; }