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; }
CMobSkillState::CMobSkillState(CMobEntity* PEntity, uint16 targid, uint16 wsid) : CState(PEntity, targid), m_PEntity(PEntity) { auto skill = battleutils::GetMobSkill(wsid); if (!skill) { throw CStateInitException(nullptr); } if (m_PEntity->StatusEffectContainer->HasStatusEffect({EFFECT_AMNESIA, EFFECT_IMPAIRMENT})) { throw CStateInitException(nullptr); } auto PTarget = m_PEntity->IsValidTarget(m_targid, skill->getValidTargets(), m_errorMsg); if (!PTarget || m_errorMsg) { throw CStateInitException(std::move(m_errorMsg)); } m_PSkill = std::make_unique<CMobSkill>(*skill); m_castTime = std::chrono::milliseconds(m_PSkill->getActivationTime()); if (m_castTime > 0s) { action_t action; action.id = m_PEntity->id; action.actiontype = ACTION_MOBABILITY_START; actionList_t& actionList = action.getNewActionList(); actionList.ActionTargetID = PTarget->id; actionTarget_t& actionTarget = actionList.getNewActionTarget(); actionTarget.reaction = REACTION_NONE; actionTarget.speceffect = SPECEFFECT_NONE; actionTarget.animation = 0; actionTarget.param = m_PSkill->getID(); actionTarget.messageID = 43; m_PEntity->loc.zone->PushPacket(m_PEntity, CHAR_INRANGE, new CActionPacket(action)); } m_PEntity->PAI->EventHandler.triggerListener("WEAPONSKILL_STATE_ENTER", m_PEntity, m_PSkill->getID()); SpendCost(); }
void CMobEntity::OnMobSkillFinished(CMobSkillState& state, action_t& action) { auto PSkill = state.GetSkill(); auto PTarget = static_cast<CBattleEntity*>(state.GetTarget()); static_cast<CMobController*>(PAI->GetController())->TapDeaggroTime(); // store the skill used m_UsedSkillIds[PSkill->getID()] = GetMLevel(); PAI->TargetFind->reset(); float distance = PSkill->getDistance(); uint8 findFlags = 0; if (PSkill->getFlag() & SKILLFLAG_HIT_ALL) { findFlags |= FINDFLAGS_HIT_ALL; } // Mob buff abilities also hit monster's pets if (PSkill->getValidTargets() == TARGET_SELF) { findFlags |= FINDFLAGS_PET; } action.id = id; if (objtype == TYPE_PET && static_cast<CPetEntity*>(this)->getPetType() == PETTYPE_JUG_PET && static_cast<CPetEntity*>(this)->getPetType() == PETTYPE_AUTOMATON) action.actiontype = ACTION_PET_MOBABILITY_FINISH; else if (PSkill->getID() < 256) action.actiontype = ACTION_WEAPONSKILL_FINISH; else action.actiontype = ACTION_MOBABILITY_FINISH; action.actionid = PSkill->getID(); if (PTarget && PAI->TargetFind->isWithinRange(&PTarget->loc.p, distance)) { if (PSkill->isAoE()) { PAI->TargetFind->findWithinArea(PTarget, (AOERADIUS)PSkill->getAoe(), PSkill->getRadius(), findFlags); } else if (PSkill->isConal()) { float angle = 45.0f; PAI->TargetFind->findWithinCone(PTarget, distance, angle, findFlags); } else { PAI->TargetFind->findSingleTarget(PTarget, findFlags); } } else { action.actiontype = ACTION_MOBABILITY_INTERRUPT; actionList_t& actionList = action.getNewActionList(); actionList.ActionTargetID = id; actionTarget_t& actionTarget = actionList.getNewActionTarget(); actionTarget.animation = PSkill->getID(); return; } uint16 actionsLength = PAI->TargetFind->m_targets.size(); PSkill->setTotalTargets(actionsLength); PSkill->setTP(state.GetSpentTP()); PSkill->setHPP(GetHPP()); uint16 msg = 0; uint16 defaultMessage = PSkill->getMsg(); bool first {true}; for (auto&& PTarget : PAI->TargetFind->m_targets) { actionList_t& list = action.getNewActionList(); list.ActionTargetID = PTarget->id; actionTarget_t& target = list.getNewActionTarget(); list.ActionTargetID = PTarget->id; target.reaction = REACTION_HIT; target.speceffect = SPECEFFECT_HIT; target.animation = PSkill->getAnimationID(); target.messageID = PSkill->getMsg(); // reset the skill's message back to default PSkill->setMsg(defaultMessage); if (objtype == TYPE_PET && static_cast<CPetEntity*>(this)->getPetType() != PETTYPE_JUG_PET) { target.animation = PSkill->getPetAnimationID(); target.param = luautils::OnPetAbility(PTarget, this, PSkill, PMaster, &action); } else { target.param = luautils::OnMobWeaponSkill(PTarget, this, PSkill); } if (msg == 0) { msg = PSkill->getMsg(); } else { msg = PSkill->getAoEMsg(); } target.messageID = msg; if (PSkill->hasMissMsg()) { target.reaction = REACTION_MISS; target.speceffect = SPECEFFECT_NONE; if (msg = PSkill->getAoEMsg()) msg = 282; } else { target.reaction = REACTION_HIT; } if (target.speceffect & SPECEFFECT_HIT) { target.speceffect = SPECEFFECT_RECOIL; target.knockback = PSkill->getKnockback(); if (first && (PSkill->getSkillchain() != 0)) { CWeaponSkill* PWeaponSkill = battleutils::GetWeaponSkill(PSkill->getSkillchain()); if (PWeaponSkill) { SUBEFFECT effect = battleutils::GetSkillChainEffect(PTarget, PWeaponSkill); if (effect != SUBEFFECT_NONE) { int32 skillChainDamage = battleutils::TakeSkillchainDamage(this, PTarget, target.param); if (skillChainDamage < 0) { target.addEffectParam = -skillChainDamage; target.addEffectMessage = 384 + effect; } else { target.addEffectParam = skillChainDamage; target.addEffectMessage = 287 + effect; } target.additionalEffect = effect; } } first = false; } } PTarget->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DETECTABLE); } }
void CAIPetDummy::ActionAbilityStart() { if (m_PPet->StatusEffectContainer->HasPreventActionEffect()) { return; } if (m_PPet->objtype == TYPE_MOB && m_PPet->PMaster->objtype == TYPE_PC) { if (m_MasterCommand == MASTERCOMMAND_SIC && m_PPet->health.tp >= 1000 && m_PBattleTarget != nullptr) { m_MasterCommand = MASTERCOMMAND_NONE; CMobEntity* PMob = (CMobEntity*)m_PPet->PMaster->PPet; std::vector<uint16> MobSkills = battleutils::GetMobSkillList(PMob->getMobMod(MOBMOD_SKILL_LIST)); if (MobSkills.size() > 0) { std::shuffle(MobSkills.begin(), MobSkills.end(), dsprand::mt()); for (auto&& skillid : MobSkills) { auto PMobSkill = battleutils::GetMobSkill(skillid); if (PMobSkill && luautils::OnMobSkillCheck(m_PBattleTarget, m_PPet, PMobSkill) == 0) { SetCurrentMobSkill(PMobSkill); break; } } // could not find skill if (!GetCurrentMobSkill()) { TransitionBack(true); return; } preparePetAbility(m_PBattleTarget); return; } return; } } if (m_PPet->getPetType() == PETTYPE_JUG_PET) { if (m_MasterCommand == MASTERCOMMAND_SIC && m_PPet->health.tp >= 1000 && m_PBattleTarget != nullptr) { //choose random tp move m_MasterCommand = MASTERCOMMAND_NONE; if (m_PPet->PetSkills.size() > 0) { auto PMobSkill = battleutils::GetMobSkill(m_PPet->PetSkills.at(dsprand::GetRandomNumber(m_PPet->PetSkills.size()))); if (PMobSkill) { SetCurrentMobSkill(PMobSkill); preparePetAbility(m_PBattleTarget); return; } } } } else if (m_PPet->getPetType() == PETTYPE_AVATAR) { for (int i = 0; i < m_PPet->PetSkills.size(); i++) { auto PMobSkill = battleutils::GetMobSkill(m_PPet->PetSkills.at(i)); if (PMobSkill && PMobSkill->getAnimationTime() == m_MasterCommand) { SetCurrentMobSkill(PMobSkill); m_MasterCommand = MASTERCOMMAND_NONE; preparePetAbility(m_PPet); return; } } m_MasterCommand = MASTERCOMMAND_NONE; } else if (m_PPet->getPetType() == PETTYPE_WYVERN) { WYVERNTYPE wyverntype = m_PPet->getWyvernType(); if (m_MasterCommand == MASTERCOMMAND_ELEMENTAL_BREATH && (wyverntype == WYVERNTYPE_MULTIPURPOSE || wyverntype == WYVERNTYPE_OFFENSIVE)) { m_MasterCommand = MASTERCOMMAND_NONE; //offensive or multipurpose wyvern if (m_PBattleTarget != nullptr) { //prepare elemental breaths int skip = dsprand::GetRandomNumber(6); int hasSkipped = 0; for (int i = 0; i < m_PPet->PetSkills.size(); i++) { auto PMobSkill = battleutils::GetMobSkill(m_PPet->PetSkills.at(i)); if (PMobSkill && PMobSkill->getValidTargets() == TARGET_ENEMY) { if (hasSkipped == skip) { SetCurrentMobSkill(PMobSkill); break; } else { hasSkipped++; } } } preparePetAbility(m_PBattleTarget); return; } } else if (m_MasterCommand == MASTERCOMMAND_HEALING_BREATH && (wyverntype == WYVERNTYPE_DEFENSIVE || wyverntype == WYVERNTYPE_MULTIPURPOSE)) { m_MasterCommand = MASTERCOMMAND_NONE; m_PBattleSubTarget = nullptr; //TODO: CHECK FOR STATUS EFFECTS FOR REMOVE- BREATH (higher priority than healing breaths) // if(m_PPet->PMaster->PParty==nullptr){//solo with master-kun CItemArmor* masterHeadItem = ((CCharEntity*)(m_PPet->PMaster))->getEquip(SLOT_HEAD); uint16 masterHead = masterHeadItem ? masterHeadItem->getID() : 0; // Determine what the required HP percentage will need to be // at or under in order for healing breath to activate. uint8 requiredHPP = 0; if (((CCharEntity*)(m_PPet->PMaster))->objtype == TYPE_PC && (masterHead == 12519 || masterHead == 15238)) { //Check for player & AF head, or +1 if (wyverntype == WYVERNTYPE_DEFENSIVE) { //healer wyvern requiredHPP = 50; } else if (wyverntype == WYVERNTYPE_MULTIPURPOSE) { //hybrid wyvern requiredHPP = 33; } } else { if (wyverntype == WYVERNTYPE_DEFENSIVE) { //healer wyvern requiredHPP = 33; } else if (wyverntype == WYVERNTYPE_MULTIPURPOSE) { //hybrid wyvern requiredHPP = 25; } } // Only attempt to find a target if there is an HP percentage to calculate. if (requiredHPP) { CBattleEntity* master = m_PPet->PMaster; // Check the master first. if (master->GetHPP() <= requiredHPP) { m_PBattleSubTarget = master; } // Otherwise if this is a healer wyvern, and the member is in a party // check all of the party members who qualify. else if (wyverntype == WYVERNTYPE_DEFENSIVE && master->PParty != nullptr) { master->ForParty([this, requiredHPP](CBattleEntity* PTarget) { if (PTarget->GetHPP() <= requiredHPP) { m_PBattleSubTarget = PTarget; } }); } } if (m_PBattleSubTarget != nullptr) { //target to heal //get highest breath for wyverns level m_PMobSkill = nullptr; for (int i = 0; i < m_PPet->PetSkills.size(); i++) { auto PMobSkill = battleutils::GetMobSkill(m_PPet->PetSkills.at(i)); if (PMobSkill && PMobSkill->getValidTargets() == TARGET_PLAYER_PARTY) { if (PMobSkill->getID() == 638 && m_PPet->PMaster->GetMLevel() < 20) { //can only using hb1 SetCurrentMobSkill(PMobSkill); break; } else if (PMobSkill->getID() == 639 && m_PPet->PMaster->GetMLevel() < 40) { //can only using hb2 SetCurrentMobSkill(PMobSkill); break; } else if (PMobSkill->getID() == 640 && m_PPet->PMaster->GetMLevel() >= 40) { //can only using hb3 SetCurrentMobSkill(PMobSkill); break; } } } preparePetAbility(m_PBattleSubTarget); return; } } } TransitionBack(true); }