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::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; } }
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; } } } } } } } }
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(); } } } }