bool CAbilityState::CanUseAbility() { if (m_PEntity->objtype == TYPE_MOB || m_PEntity->objtype == TYPE_PET) return true; if (m_PEntity->objtype == TYPE_PC) { auto PAbility = GetAbility(); auto PChar = static_cast<CCharEntity*>(m_PEntity); if (PChar->PRecastContainer->HasRecast(RECAST_ABILITY, PAbility->getRecastId(), PAbility->getRecastTime())) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_WAIT_LONGER)); return false; } if (PChar->StatusEffectContainer->HasStatusEffect({EFFECT_AMNESIA, EFFECT_IMPAIRMENT})) { PChar->pushPacket(new CMessageBasicPacket(PChar, PChar, 0, 0, MSGBASIC_UNABLE_TO_USE_JA2)); return false; } std::unique_ptr<CMessageBasicPacket> errMsg; auto PTarget = GetTarget(); if (PChar->IsValidTarget(PTarget->targid, PAbility->getValidTarget(), errMsg)) { if (PChar != PTarget && distance(PChar->loc.p, PTarget->loc.p) > PAbility->getRange()) { PChar->pushPacket(new CMessageBasicPacket(PChar, PTarget, 0, 0, MSGBASIC_TOO_FAR_AWAY)); return false; } if (!m_PEntity->PAI->TargetFind->canSee(&PTarget->loc.p)) { m_errorMsg = std::make_unique<CMessageBasicPacket>(m_PEntity, PTarget, PAbility->getID(), 0, MSGBASIC_CANNOT_PERFORM_ACTION); return false; } if (PAbility->getID() >= ABILITY_HEALING_RUBY) { // Blood pact MP costs are stored under animation ID if (PChar->health.mp < PAbility->getAnimationID()) { PChar->pushPacket(new CMessageBasicPacket(PChar, PTarget, 0, 0, MSGBASIC_UNABLE_TO_USE_JA)); return false; } } CBaseEntity* PMsgTarget = PChar; int32 errNo = luautils::OnAbilityCheck(PChar, PTarget, PAbility, &PMsgTarget); if (errNo != 0) { PChar->pushPacket(new CMessageBasicPacket(PChar, PMsgTarget, PAbility->getID() + 16, PAbility->getID(), errNo)); return false; } return true; } return false; } return true; }
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); } }