void CAIContainer::MobSkill(uint16 targid, uint16 wsid) { auto AIController = dynamic_cast<CAIController*>(Controller.get()); if (AIController) { AIController->MobSkill(targid, wsid); } }
bool CAIContainer::MobSkill(uint16 targid, uint16 wsid) { auto AIController = dynamic_cast<CMobController*>(Controller.get()); if (AIController) { return AIController->MobSkill(targid, wsid); } return false; }
bool CAutomatonController::TryShieldBash() { CState* PState = PTarget->PAI->GetCurrentState(); if (m_shieldbashCooldown > 0s && PState && PState->CanInterrupt() && m_Tick > m_LastShieldBashTime + (m_shieldbashCooldown - std::chrono::seconds(PAutomaton->getMod(Mod::AUTO_SHIELD_BASH_DELAY)))) { return MobSkill(PTarget->targid, m_ShieldBashAbility); } return false; }
bool CMobController::TrySpecialSkill() { // get my special skill CMobSkill* PSpecialSkill = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_SPECIAL_SKILL)); CBattleEntity* PAbilityTarget = nullptr; m_LastSpecialTime = m_Tick; if (PSpecialSkill == nullptr) { ShowError("CAIMobDummy::ActionSpawn Special skill was set but not found! (%d)\n", PMob->getMobMod(MOBMOD_SPECIAL_SKILL)); return false; } if (!IsWeaponSkillEnabled()) { return false; } if ((PMob->m_specialFlags & SPECIALFLAG_HIDDEN) && !PMob->IsNameHidden()) { return false; } if (PSpecialSkill->getValidTargets() & TARGET_SELF) { PAbilityTarget = PMob; } else if (PTarget != nullptr) { // distance check for special skill float currentDistance = distance(PMob->loc.p, PTarget->loc.p); if (currentDistance <= PSpecialSkill->getDistance()) { PAbilityTarget = PTarget; } else { return false; } } else { return false; } if (luautils::OnMobSkillCheck(PAbilityTarget, PMob, PSpecialSkill) == 0) { return MobSkill(PAbilityTarget->targid, PSpecialSkill->getID()); } return false; }
bool CAIController::MobSkill(int wsList) { /* #TODO: mob 2 hours, etc */ if (!wsList) wsList = PMob->getMobMod(MOBMOD_SKILL_LIST); auto skillList {battleutils::GetMobSkillList(wsList)}; if (skillList.empty()) { return false; } std::shuffle(skillList.begin(), skillList.end(), dsprand::mt()); CBattleEntity* PActionTarget {nullptr}; for (auto skillid : skillList) { auto PMobSkill {battleutils::GetMobSkill(skillid)}; if (!PMobSkill) { continue; } if (PMobSkill->getValidTargets() == TARGET_ENEMY) //enemy { PActionTarget = PTarget; } else if (PMobSkill->getValidTargets() == TARGET_SELF) //self { PActionTarget = PMob; } else { continue; } float currentDistance = distance(PMob->loc.p, PActionTarget->loc.p); if (!PMobSkill->isTwoHour() && luautils::OnMobSkillCheck(PActionTarget, PMob, PMobSkill) == 0) //A script says that the move in question is valid { if (currentDistance <= PMobSkill->getDistance()) { MobSkill(PActionTarget->targid, PMobSkill->getID()); break; } } } return false; }
void CMobController::Move() { if (!PMob->PAI->CanFollowPath()) { return; } float currentDistance = distance(PMob->loc.p, PTarget->loc.p); if (PMob->PAI->PathFind->IsFollowingScriptedPath() && PMob->PAI->CanFollowPath()) { PMob->PAI->PathFind->FollowPath(); return; } // attempt to teleport if (PMob->getMobMod(MOBMOD_TELEPORT_TYPE) == 1) { if (m_Tick >= m_LastSpecialTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_TELEPORT_CD))) { CMobSkill* teleportBegin = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_TELEPORT_START)); if (teleportBegin) { m_LastSpecialTime = m_Tick; MobSkill(PMob->targid, teleportBegin->getID()); } } } bool move = PMob->PAI->PathFind->IsFollowingPath(); float attack_range = PMob->m_ModelSize; if (PMob->getMobMod(MOBMOD_ATTACK_SKILL_LIST) > 0) { auto skillList {battleutils::GetMobSkillList(PMob->getMobMod(MOBMOD_ATTACK_SKILL_LIST))}; if (!skillList.empty()) { auto skill {battleutils::GetMobSkill(skillList.front())}; if (skill) { attack_range = skill->getDistance(); } } } if (PMob->getMobMod(MOBMOD_SHARE_POS) > 0) { CMobEntity* posShare = (CMobEntity*)PMob->GetEntity(PMob->getMobMod(MOBMOD_SHARE_POS) + PMob->targid, TYPE_MOB); PMob->loc = posShare->loc; } else if (((distance(PMob->loc.p, PTarget->loc.p) > attack_range - 0.2f) || move) && PMob->PAI->CanFollowPath()) { //#TODO: can this be moved to scripts entirely? if (PMob->getMobMod(MOBMOD_DRAW_IN) > 0) { if (currentDistance >= PMob->m_ModelSize * 2) battleutils::DrawIn(PTarget, PMob, PMob->m_ModelSize - 0.2f); } if (PMob->speed != 0 && PMob->getMobMod(MOBMOD_NO_MOVE) == 0 && m_Tick >= m_LastSpecialTime) { // attempt to teleport to target (if in range) if (PMob->getMobMod(MOBMOD_TELEPORT_TYPE) == 2) { CMobSkill* teleportBegin = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_TELEPORT_START)); if (teleportBegin && currentDistance <= teleportBegin->getDistance()) { MobSkill(PMob->targid, teleportBegin->getID()); m_LastSpecialTime = m_Tick; return; } } else if (CanMoveForward(currentDistance)) { if (!PMob->PAI->PathFind->IsFollowingPath() || distanceSquared(PMob->PAI->PathFind->GetDestination(), PTarget->loc.p) > 10) { //path to the target if we don't have a path already PMob->PAI->PathFind->PathInRange(PTarget->loc.p, attack_range - 0.2f, PATHFLAG_WALLHACK | PATHFLAG_RUN); } PMob->PAI->PathFind->FollowPath(); if (!PMob->PAI->PathFind->IsFollowingPath()) { //arrived at target - move if there is another mob under me if (PTarget->objtype == TYPE_PC) { for (auto PSpawnedMob : static_cast<CCharEntity*>(PTarget)->SpawnMOBList) { if (PSpawnedMob.second != PMob && !PSpawnedMob.second->PAI->PathFind->IsFollowingPath() && distance(PSpawnedMob.second->loc.p, PMob->loc.p) < 1.f) { auto angle = getangle(PMob->loc.p, PTarget->loc.p) + 64; position_t new_pos {0, PMob->loc.p.x - (cosf(rotationToRadian(angle)) * 1.5f), PTarget->loc.p.y, PMob->loc.p.z + (sinf(rotationToRadian(angle)) * 1.5f), 0}; if (PMob->PAI->PathFind->ValidPosition(new_pos)) { PMob->PAI->PathFind->PathTo(new_pos, PATHFLAG_WALLHACK | PATHFLAG_RUN); } break; } } } } } else { FaceTarget(); } } } else { FaceTarget(); } }
void CMobController::DoCombatTick(time_point tick) { HandleEnmity(); PTarget = static_cast<CBattleEntity*>(PMob->GetEntity(PMob->GetBattleTargetID())); if (TryDeaggro()) { Disengage(); return; } TryLink(); float currentDistance = distance(PMob->loc.p, PTarget->loc.p); luautils::OnMobFight(PMob, PTarget); // Try to spellcast (this is done first so things like Chainspell spam is prioritised over TP moves etc. if (IsSpecialSkillReady(currentDistance) && TrySpecialSkill()) { return; } else if (IsSpellReady(currentDistance) && TryCastSpell()) { return; } else if (m_Tick >= m_LastMobSkillTime && dsprand::GetRandomNumber(100) < PMob->TPUseChance() && MobSkill()) { return; } Move(); }
void CMobController::DoCombatTick(time_point tick) { HandleEnmity(); PTarget = static_cast<CBattleEntity*>(PMob->GetEntity(PMob->GetBattleTargetID())); if (TryDeaggro()) { Disengage(); return; } TryLink(); float currentDistance = distance(PMob->loc.p, PTarget->loc.p); if (!(PMob->m_Behaviour & BEHAVIOUR_NO_TURN)) { PMob->PAI->PathFind->LookAt(PTarget->loc.p); } luautils::OnMobFight(PMob, PTarget); // Try to spellcast (this is done first so things like Chainspell spam is prioritised over TP moves etc. if (IsSpecialSkillReady(currentDistance) && TrySpecialSkill()) { return; } else if (IsSpellReady(currentDistance) && TryCastSpell()) { return; } else if (m_Tick >= m_LastMobSkillTime && dsprand::GetRandomNumber(100) < PMob->TPUseChance() && MobSkill()) { return; } if (PMob->PAI->PathFind->IsFollowingScriptedPath() && PMob->PAI->CanFollowPath()) { PMob->PAI->PathFind->FollowPath(); return; } // attempt to teleport if (PMob->getMobMod(MOBMOD_TELEPORT_TYPE) == 1) { if (m_Tick >= m_LastSpecialTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_TELEPORT_CD))) { CMobSkill* teleportBegin = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_TELEPORT_START)); if (teleportBegin) { m_LastSpecialTime = m_Tick; MobSkill(PMob->targid, teleportBegin->getID()); } } } bool move = PMob->PAI->PathFind->IsFollowingPath(); float attack_range = PMob->m_ModelSize; if (PMob->getMobMod(MOBMOD_ATTACK_SKILL_LIST) > 0) { auto skillList {battleutils::GetMobSkillList(PMob->getMobMod(MOBMOD_ATTACK_SKILL_LIST))}; if (!skillList.empty()) { auto skill {battleutils::GetMobSkill(skillList.front())}; if (skill) { attack_range = skill->getDistance(); } } } if (PMob->getMobMod(MOBMOD_SHARE_POS) > 0) { CMobEntity* posShare = (CMobEntity*)PMob->GetEntity(PMob->getMobMod(MOBMOD_SHARE_POS) + PMob->targid, TYPE_MOB); PMob->loc = posShare->loc; } else if (((distance(PMob->loc.p, PTarget->loc.p) > attack_range - 0.2f) || move) && PMob->PAI->CanFollowPath()) { //#TODO: can this be moved to scripts entirely? if (PMob->getMobMod(MOBMOD_DRAW_IN) > 0) { if (currentDistance >= PMob->m_ModelSize * 2) battleutils::DrawIn(PTarget, PMob, PMob->m_ModelSize - 0.2f); } if (PMob->speed != 0 && m_Tick >= m_LastSpecialTime) { // attempt to teleport to target (if in range) if (PMob->getMobMod(MOBMOD_TELEPORT_TYPE) == 2) { CMobSkill* teleportBegin = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_TELEPORT_START)); if (teleportBegin && currentDistance <= teleportBegin->getDistance()) { MobSkill(PMob->targid, teleportBegin->getID()); m_LastSpecialTime = m_Tick; return; } } else if (CanMoveForward(currentDistance)) { if (!PMob->PAI->PathFind->IsFollowingPath()) { //path to the target if we don't have a path already PMob->PAI->PathFind->PathInRange(PTarget->loc.p, attack_range - 0.2f, PATHFLAG_WALLHACK | PATHFLAG_RUN); } PMob->PAI->PathFind->FollowPath(); if (!PMob->PAI->PathFind->IsFollowingPath()) { //arrived at target - move if there is another mob under me if (PTarget->objtype == TYPE_PC) { for (auto PSpawnedMob : static_cast<CCharEntity*>(PTarget)->SpawnMOBList) { if (PSpawnedMob.second != PMob && !PSpawnedMob.second->PAI->PathFind->IsFollowingPath() && distance(PSpawnedMob.second->loc.p, PMob->loc.p) < 1.f) { auto angle = getangle(PMob->loc.p, PTarget->loc.p) + 64; position_t new_pos {0, PMob->loc.p.x - (cosf(rotationToRadian(angle)) * 1.5f), PTarget->loc.p.y, PMob->loc.p.z + (sinf(rotationToRadian(angle)) * 1.5f), 0}; if (PMob->PAI->PathFind->ValidPosition(new_pos)) { PMob->PAI->PathFind->PathTo(new_pos, PATHFLAG_WALLHACK | PATHFLAG_RUN); } break; } } } } } } } }
bool CAutomatonController::TryRangedAttack() // TODO: Find the animation for its ranged attack { if (m_rangedCooldown > 0s && m_Tick > m_LastRangedTime + (m_rangedCooldown - std::chrono::seconds(PAutomaton->getMod(Mod::SNAP_SHOT)))) return MobSkill(PTarget->targid, m_RangedAbility); return false; }
bool CAutomatonController::TryTPMove() { if (PAutomaton->health.tp >= 1000) { const auto& FamilySkills = battleutils::GetMobSkillList(PAutomaton->m_Family); std::vector<CMobSkill*> validSkills; //load the skills that the automaton has access to with it's skill SKILLTYPE skilltype = SKILL_AME; if (PAutomaton->getFrame() == FRAME_SHARPSHOT) skilltype = SKILL_ARA; for (auto skillid : FamilySkills) { auto PSkill = battleutils::GetMobSkill(skillid); if (PSkill && PAutomaton->GetSkill(skilltype) > PSkill->getParam() && PSkill->getParam() != -1 && distance(PAutomaton->loc.p, PTarget->loc.p) < PSkill->getRadius()) { validSkills.push_back(PSkill); } } int16 currentSkill = -1; CMobSkill* PWSkill = nullptr; int8 currentManeuvers = -1; bool attemptChain = (PAutomaton->getMod(Mod::AUTO_TP_EFFICIENCY) != 0); if (attemptChain) { CStatusEffect* PSCEffect = PTarget->StatusEffectContainer->GetStatusEffect(EFFECT_SKILLCHAIN, 0); if (PSCEffect) { std::list<SKILLCHAIN_ELEMENT> resonanceProperties; if (PSCEffect->GetTier() == 0) { if (PSCEffect->GetStartTime() + 3s < m_Tick) { if (PSCEffect->GetPower()) { CWeaponSkill* PWeaponSkill = battleutils::GetWeaponSkill(PSCEffect->GetPower()); resonanceProperties.push_back((SKILLCHAIN_ELEMENT)PWeaponSkill->getPrimarySkillchain()); resonanceProperties.push_back((SKILLCHAIN_ELEMENT)PWeaponSkill->getSecondarySkillchain()); resonanceProperties.push_back((SKILLCHAIN_ELEMENT)PWeaponSkill->getTertiarySkillchain()); } else { CBlueSpell* oldSpell = (CBlueSpell*)spell::GetSpell(static_cast<SpellID>(PSCEffect->GetSubPower())); resonanceProperties.push_back((SKILLCHAIN_ELEMENT)oldSpell->getPrimarySkillchain()); resonanceProperties.push_back((SKILLCHAIN_ELEMENT)oldSpell->getSecondarySkillchain()); } } } else { resonanceProperties.push_back((SKILLCHAIN_ELEMENT)PSCEffect->GetPower()); } for (auto PSkill : validSkills) { if (PSkill->getParam() > currentSkill) { std::list<SKILLCHAIN_ELEMENT> skillProperties; skillProperties.push_back((SKILLCHAIN_ELEMENT)PSkill->getPrimarySkillchain()); skillProperties.push_back((SKILLCHAIN_ELEMENT)PSkill->getSecondarySkillchain()); skillProperties.push_back((SKILLCHAIN_ELEMENT)PSkill->getTertiarySkillchain()); if (battleutils::FormSkillchain(resonanceProperties, skillProperties) != SC_NONE) { currentManeuvers = 1; currentSkill = PSkill->getParam(); PWSkill = PSkill; } } } } } if (!attemptChain || (currentManeuvers == -1 && PAutomaton->PMaster && PAutomaton->PMaster->health.tp < PAutomaton->getMod(Mod::AUTO_TP_EFFICIENCY))) { for (auto PSkill : validSkills) { int8 maneuvers = luautils::OnMobAutomatonSkillCheck(PTarget, PAutomaton, PSkill); if (maneuvers > -1 && (maneuvers > currentManeuvers || (maneuvers == currentManeuvers && PSkill->getParam() > currentSkill))) { currentManeuvers = maneuvers; currentSkill = PSkill->getParam(); PWSkill = PSkill; } } } // No WS was chosen (waiting on master's TP to skillchain probably) if (currentManeuvers == -1) return false; if (PWSkill) return MobSkill(PTarget->targid, PWSkill->getID()); } return false; }
void CAIController::DoCombatTick(time_point tick) { HandleEnmity(); PTarget = static_cast<CBattleEntity*>(PMob->loc.zone->GetEntity(PMob->GetBattleTargetID())); if (TryDeaggro()) { Disengage(); return; } TryLink(); float currentDistance = distance(PMob->loc.p, PTarget->loc.p); if (!(PMob->m_Behaviour & BEHAVIOUR_NO_TURN)) { PMob->PAI->PathFind->LookAt(PTarget->loc.p); } luautils::OnMobFight(PMob, PTarget); // Try to spellcast (this is done first so things like Chainspell spam is prioritised over TP moves etc. if (PMob->getMobMod(MOBMOD_SPECIAL_SKILL) != 0 && !PMob->StatusEffectContainer->HasStatusEffect(EFFECT_CHAINSPELL) && (m_Tick >= m_LastSpecialTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_SPECIAL_COOL))) && TrySpecialSkill()) { return; } else if ((m_Tick >= m_LastMagicTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_MAGIC_COOL))) && TryCastSpell()) { return; } else if (m_Tick >= m_LastMobSkillTime && dsprand::GetRandomNumber(100) < PMob->TPUseChance() && MobSkill()) { return; } if (PMob->PAI->PathFind->IsFollowingScriptedPath() && PMob->PAI->CanFollowPath()) { PMob->PAI->PathFind->FollowPath(); return; } // attempt to teleport if (PMob->getMobMod(MOBMOD_TELEPORT_TYPE) == 1) { if (m_Tick >= m_LastSpecialTime + std::chrono::milliseconds(PMob->getBigMobMod(MOBMOD_TELEPORT_CD))) { CMobSkill* teleportBegin = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_TELEPORT_START)); if (teleportBegin) { MobSkill(PMob->targid, teleportBegin->getID()); } } } bool move = PMob->PAI->PathFind->IsFollowingPath(); //If using mobskills instead of attacks, calculate distance to move and ability to use here if (PMob->getMobMod(MOBMOD_ATTACK_SKILL_LIST)) { auto WeaponDelay = std::chrono::milliseconds(PMob->GetWeaponDelay(false)); if (IsAutoAttackEnabled() && m_Tick > m_LastActionTime + WeaponDelay) { MobSkill(PMob->getMobMod(MOBMOD_ATTACK_SKILL_LIST)); } } if (PMob->getMobMod(MOBMOD_SHARE_POS) > 0) { CMobEntity* posShare = (CMobEntity*)PMob->GetEntity(PMob->getMobMod(MOBMOD_SHARE_POS) + PMob->targid, TYPE_MOB); PMob->loc = posShare->loc; } else if (((distance(PMob->loc.p, PTarget->loc.p) > PMob->m_ModelSize) || move) && PMob->PAI->CanFollowPath()) { //#TODO: can this be moved to scripts entirely? if (PMob->getMobMod(MOBMOD_DRAW_IN) > 0) { if (currentDistance >= PMob->m_ModelSize * 2) battleutils::DrawIn(PTarget, PMob, PMob->m_ModelSize - 0.2f); } if (PMob->speed != 0 && m_Tick >= m_LastSpecialTime) { // attempt to teleport to target (if in range) if (PMob->getMobMod(MOBMOD_TELEPORT_TYPE) == 2) { CMobSkill* teleportBegin = battleutils::GetMobSkill(PMob->getMobMod(MOBMOD_TELEPORT_START)); if (teleportBegin && currentDistance <= teleportBegin->getDistance()) { MobSkill(PMob->targid, teleportBegin->getID()); return; } } else if (!(PMob->m_Behaviour & BEHAVIOUR_STANDBACK && currentDistance < 20) && !(PMob->getMobMod(MOBMOD_HP_STANDBACK) == 1 && currentDistance < 20 && PMob->GetHPP() > 70) && !(PMob->getMobMod(MOBMOD_SPAWN_LEASH) > 0 && distance(PMob->loc.p, PMob->m_SpawnPoint) > PMob->getMobMod(MOBMOD_SPAWN_LEASH))) { PMob->PAI->PathFind->PathAround(PTarget->loc.p, 2.0f, PATHFLAG_WALLHACK | PATHFLAG_RUN); PMob->PAI->PathFind->FollowPath(); } } } }