local BattleConst = require "app/module/battle/battle_const" local BattleHelper = require "app/module/battle/helper/battle_helper" local BattleBuffHandle = require "app/module/battle/helper/battle_buff_handle" local BattlePassive = require "app/module/battle/helper/battle_passive" local BattleUnitComp = class("BattleUnitComp", LuaComponent) local UNIT_STATE = BattleConst.UNIT_STATE local SIDE_ATK = BattleConst.SIDE_ATK local SPINE_ANIMATION_NAME = BattleConst.SPINE_ANIMATION_NAME local DEFAULT_FACTOR = BattleConst.DEFAULT_FACTOR local PASSIVE_EVENT = BattleConst.PASSIVE_EVENT local HURT_STATE_CRIT = BattleConst.HURT_STATE_CRIT local EFFECT_TYPE = BattleConst.EFFECT_TYPE local TIME_FACTOR = BattleConst.TIME_FACTOR function BattleUnitComp:ctor() end function BattleUnitComp:initPosition() if self.unitEntity:getSide() == SIDE_ATK then self.baseObject:setLocalPosition(-BattleConst.INIT_POS_X, 0, 0) self.body:setLocalScaleX(1) self.direction = 1 else self.baseObject:setLocalPosition(BattleConst.INIT_POS_X, 0, 0) self.body:setLocalScaleX(-1) self.direction = -1 end self.isOutside = false end function BattleUnitComp:playBorn() self:changeState(UNIT_STATE.IDLE) end function BattleUnitComp:playSwitchIn() self.baseObject:getTransform():SetAsLastSibling() self:changeState(UNIT_STATE.SWITCH_IN) end function BattleUnitComp:playSwitchOut() self:changeState(UNIT_STATE.SWITCH_OUT) end function BattleUnitComp:getModelId() return self.modelId end function BattleUnitComp:getMatchType() return self.unitEntity:getMatchType() end function BattleUnitComp:getIsBoss() return self.unitEntity:getIsBoss() end function BattleUnitComp:getIsLimit() return self.unitEntity:getIsLimit() end function BattleUnitComp:setTeam(team) self.team = team end function BattleUnitComp:_initBase() self.isClear = false self.isMove = false self.deadTime = 0 self.attackTime = 0 self.currAttackDuration = 0 self.currAttackKeyTime = 0 self.switchTime = 0 self.isPlayHurt = 0 self.attackDurationMap = {} self.attackKeyFrameTimeMap = {} self.shieldBuffList = {} self.loopFxMap = {} self.activeSkillIndex = nil self.currActiveSkill = nil self.targetX = nil self.assistingDmgAddition = 0 self.attackCount = 0 self.currState = UNIT_STATE.INIT self.isOutside = false end function BattleUnitComp:initWithEntity(modelId, entity, battleController, target) self.modelId = modelId self.unitEntity = entity self.battleController = battleController self.target = target self.body = self.baseObject:getChildByName("body") self.side = entity:getSide() self:_initBase() self:initPosition() self:initPassiveSkills() self:playBorn() end function BattleUnitComp:prepare() self:checkPassiveEvent(PASSIVE_EVENT.ON_UNIT_PREPARE_OVER, self) self:checkPassiveEvent(PASSIVE_EVENT.HP_LOWER_THAN, nil, self.unitEntity:getHpPercent()) end function BattleUnitComp:resetBeforeAttack() self.attackCount = 0 self.baseObject:getTransform():SetAsLastSibling() end function BattleUnitComp:initPassiveSkills() local pasSkills = self.unitEntity:getPassiveSkills() if pasSkills and #pasSkills > 0 then if self.passiveSkills == nil then self.passiveSkills = {} else self:removeAllPassiveSkills() end for k, skill in ipairs(pasSkills) do local id = skill:getPassiveTriggerId() if id then skill:clearRecordData() local skills = self.passiveSkills[id] if skills == nil then skills = {} self.passiveSkills[id] = skills end table.insert(skills, skill) end end end end function BattleUnitComp:addPassiveSkill(pasSkill) pasSkill:clearRecordData() if self.passiveSkills == nil then self.passiveSkills = {} end local id = pasSkill:getPassiveTriggerId() if id then local skills = self.passiveSkills[id] if skills == nil then skills = {} self.passiveSkills[id] = skills end table.insert(skills, pasSkill) end end function BattleUnitComp:removePassiveSkill(skill) if self.passiveSkills == nil then return end local id = skill:getPassiveTriggerId() if id then local skills = self.passiveSkills[id] if skills then for k, v in ipairs(skills) do if v == skill then table.remove(skills, k) break end end end end end function BattleUnitComp:removeAllPassiveSkills() if self.passiveSkills then for _, skills in pairs(self.passiveSkills) do local count = #skills for i = 1, count do table.remove(skills) end end end end function BattleUnitComp:addSkill(skillId) local skillEntity = self.unitEntity:addSkill(skillId) if skillEntity then if skillEntity:getIsPassiveType() then -- 添加被动 self:addPassiveSkill(skillEntity) end return skillEntity:getSid() end return 0 end function BattleUnitComp:removeSkill(skillId, sid) local skillEntity = self.unitEntity:removeSkill(skillId, sid) if skillEntity and skillEntity:getIsPassiveType() then self:removePassiveSkill(skillEntity) end end function BattleUnitComp:hideOutsideScreen() if self.unitEntity:getSide() == SIDE_ATK then self.baseObject:setLocalPosition(-GConst.UI_SCREEN_WIDTH/2 - BattleConst.UNIT_BODY_WIDTH, 0, 0) else self.baseObject:setLocalPosition(GConst.UI_SCREEN_WIDTH/2 + BattleConst.UNIT_BODY_WIDTH, 0, 0) end self.isOutside = true end function BattleUnitComp:playRunAction() self:playAnimation(SPINE_ANIMATION_NAME.MOVE, true) end function BattleUnitComp:stopRunAction() self:playAnimation(SPINE_ANIMATION_NAME.IDLE, true) end function BattleUnitComp:playAnimation(name, loop, forceRefresh) if name == SPINE_ANIMATION_NAME.HIT then self.isPlayHurt = 1 else self.isPlayHurt = 0 end self.currAnimationName = name self.baseObject:playAnimation(name, loop, forceRefresh) end function BattleUnitComp:getAnimationDuration(aniName) local duration = self.attackDurationMap[aniName] if duration == nil then duration = self.baseObject:getAnimationDuration(aniName) self.attackDurationMap[aniName] = duration end return duration or 0 end function BattleUnitComp:getAnimationKeyFrameTime(animationName) local time = self.attackKeyFrameTimeMap[animationName] if time == nil then time = self.baseObject:getAnimationKeyFrameTime(animationName) if time <= 0 then -- 容错处理 time = 0.3 end self.attackKeyFrameTimeMap[animationName] = time end return time end function BattleUnitComp:getIsCentralizedAttack() return self.team:getCentralizedAttack() end function BattleUnitComp:beforeAttack() self.team:setCentralizedAttack(true) self.battleController:setIsPauseHpProgress(true) self:checkPassiveEvent(PASSIVE_EVENT.ON_UNI_ATTACK_START, self) end function BattleUnitComp:useAssistingSkill(count, delay, callback) local skill = self.unitEntity:getAssistingSkill() if skill == nil then callback() return end local attrName = GConst.MATCH_ATTACK_NAME[self:getMatchType()] self.assistingDmgAddition = count - 1 if self.assistingDmgAddition <= 0 then self.assistingDmgAddition = 0 self.assistingDmgAddCount = 0 else self.assistingDmgAddCount = self.unitEntity:addAttr(attrName, self.assistingDmgAddition*DEFAULT_FACTOR, true) end self.actionOverCallback = callback if delay > 0 then self.waitTime = delay self.waitingState = UNIT_STATE.ASSISTING_ATTACK if not self:changeState(UNIT_STATE.WAIT) then if not self:changeState(UNIT_STATE.ASSISTING_ATTACK) then self.actionOverCallback = nil self.unitEntity:addAttr(attrName, -self.assistingDmgAddCount, false) callback() end end else if not self:changeState(UNIT_STATE.ASSISTING_ATTACK) then self.actionOverCallback = nil self.unitEntity:addAttr(attrName, -self.assistingDmgAddCount, false) callback() end end end function BattleUnitComp:useSkill(index, count, callback) self.actionOverCallback = callback self.activeSkillIndex = nil self.currActiveSkill = self.unitEntity:getAvailableActiveSkill(index) if count <= 0 then self.normalSkillCount = 0 else self.normalSkillCount = count + self.unitEntity:getNormalAttackAddCount() end if self.currActiveSkill == nil then -- 没有技能就用普攻 if self.normalSkillCount <= 0 then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() return false end if self.isOutside then self:playSwitchIn() else if not self:changeState(UNIT_STATE.NORMAL_ATTACK) then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() return false end end else if self.isOutside then self:playSwitchIn() else if not self:changeState(UNIT_STATE.SKILL_ATTACK) then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() return false end end end return true end function BattleUnitComp:useAllSkills(callback) self.actionOverCallback = callback self.normalSkillCount = self.unitEntity:getNormalSkillCount() + self.unitEntity:getNormalAttackAddCount() self.activeSkillIndex = 1 self.currActiveSkill = nil local activeSkillCount = self.unitEntity:getActiveSkillCount() while self.activeSkillIndex <= activeSkillCount do self.currActiveSkill = self.unitEntity:getAvailableActiveSkill(self.activeSkillIndex) if self.currActiveSkill then break end self.activeSkillIndex = self.activeSkillIndex + 1 end if self.currActiveSkill == nil then -- 没有技能就用普攻 if self.normalSkillCount <= 0 then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() return end if not self:changeState(UNIT_STATE.NORMAL_ATTACK) then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() end else if not self:changeState(UNIT_STATE.SKILL_ATTACK) then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() end end end function BattleUnitComp:useNormalSkill(count, callback) self.actionOverCallback = callback self.normalSkillCount = count + self.unitEntity:getNormalAttackAddCount() if not self:changeState(UNIT_STATE.NORMAL_ATTACK) then self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() end end function BattleUnitComp:addShield(num, buffEffect) self.unitEntity:addShield(num) self.team:addShield(buffEffect) end function BattleUnitComp:removeShield(buffEffect) if buffEffect == nil then return end if buffEffect.result > 0 then self.unitEntity:addShield(-buffEffect.result) buffEffect.result = 0 end self.team:removeShield(buffEffect) end function BattleUnitComp:changeState(state) if self.currState == state and not self:repeatCurrState() then return false end if self.currState == UNIT_STATE.DEAD then -- 死亡后只能去死亡状态 if state ~= UNIT_STATE.DEAD then return false end end -- 离开当前状态 if self.currState == UNIT_STATE.IDLE then self:exitIdleState() elseif self.currState == UNIT_STATE.NORMAL_ATTACK then self:exitNormalAttackState() elseif self.currState == UNIT_STATE.SKILL_ATTACK then self:exitSkillAttackState() elseif self.currState == UNIT_STATE.ASSISTING_ATTACK then self:exitAssistingAttackState() elseif self.currState == UNIT_STATE.DEAD then self:exitDeadState() elseif self.currState == UNIT_STATE.ENTER_BATTLEFIELD then self:exitEnterBattlefieldState() elseif self.currState == UNIT_STATE.SWITCH_IN then self:exitSwitchInState() elseif self.currState == UNIT_STATE.SWITCH_OUT then self:exitSwitchOutState() elseif self.currState == UNIT_STATE.WAIT then self:exitWaitState() elseif self.currState == UNIT_STATE.RECOVER_HP_WAVE then self:exitRecoverHpWaveState() end -- 进入目标状态 self.currState = state if state == UNIT_STATE.IDLE then self:enterIdleState() elseif state == UNIT_STATE.NORMAL_ATTACK then self:enterNormalAttackState() elseif state == UNIT_STATE.SKILL_ATTACK then self:enterSkillAttackState() elseif state == UNIT_STATE.ASSISTING_ATTACK then self:enterAssistingAttackState() elseif state == UNIT_STATE.DEAD then self:enterDeadState() elseif state == UNIT_STATE.BORN then self:enterBornState() elseif state == UNIT_STATE.ENTER_BATTLEFIELD then self:enterEnterBattlefieldState() elseif state == UNIT_STATE.SWITCH_IN then self:enterSwitchInState() elseif state == UNIT_STATE.SWITCH_OUT then self:enterSwitchOutState() elseif state == UNIT_STATE.WAIT then self:enterWaitState() elseif state == UNIT_STATE.RECOVER_HP_WAVE then self:enterRecoverHpWaveState() end return true end function BattleUnitComp:repeatCurrState() if self.currState == UNIT_STATE.NORMAL_ATTACK then return true elseif self.currState == UNIT_STATE.SKILL_ATTACK then return true end return false end function BattleUnitComp:updateSwitchInState(dt) self.switchTime = self.switchTime - dt if self.switchTime < 0 then if self.actionOverCallback then if self.currActiveSkill then if not self:changeState(UNIT_STATE.SKILL_ATTACK) then local callback = self.actionOverCallback self.actionOverCallback = nil self.activeSkillIndex = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() end elseif self.normalSkillCount > 0 then if not self:changeState(UNIT_STATE.NORMAL_ATTACK) then local callback = self.actionOverCallback self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() end else self:changeState(UNIT_STATE.IDLE) local callback = self.actionOverCallback self.actionOverCallback = nil self.battleController:setIsPauseHpProgress(false) self.team:setCentralizedAttack(false) callback() end else self:changeState(UNIT_STATE.IDLE) end end end function BattleUnitComp:enterSwitchInState() local aniName = SPINE_ANIMATION_NAME.BORN self.switchTime = self:getAnimationDuration(aniName) + 0.1 self:initPosition() self:playAnimation(aniName, false, true) end function BattleUnitComp:exitSwitchInState() end function BattleUnitComp:updateSwitchOutState(dt) self.switchTime = self.switchTime - dt if self.switchTime < 0 then self:changeState(UNIT_STATE.IDLE) end end function BattleUnitComp:enterSwitchOutState() local aniName = SPINE_ANIMATION_NAME.OUT self.switchTime = self:getAnimationDuration(aniName) + 0.1 self:playAnimation(aniName, false, true) end function BattleUnitComp:exitSwitchOutState() self:hideOutsideScreen() end function BattleUnitComp:updateDead(dt) self.deadTime = self.deadTime - dt if self.deadTime <= 0 then self:clear() if self.deadOverCallback then local callback = self.deadOverCallback self.deadOverCallback = nil callback() end end end function BattleUnitComp:exitDeadState() end function BattleUnitComp:enterDeadState() self:removeAllBuff() local aniName = SPINE_ANIMATION_NAME.DEAD self.deadTime = self:getAnimationDuration(aniName) + 0.1 self:playAnimation(aniName, false, false) end function BattleUnitComp:updateEnterBattlefieldState(dt) if self.isMove then local addX = dt*BattleConst.MOVE_SPEED_ENTER*self.moveDirection self.positionX = self.positionX + addX if (self.moveDirection > 0 and self.positionX >= self.targetX) or (self.moveDirection < 0 and self.positionX <= self.targetX) then self.isMove = false self.positionX = self.targetX self:changeState(UNIT_STATE.IDLE) end self.baseObject:setLocalPosition(self.positionX, 0, 0) else self.waitTime = self.waitTime - dt if self.waitTime < 0 then self:changeState(UNIT_STATE.IDLE) end end end function BattleUnitComp:exitEnterBattlefieldState() local callback = self.finishEnterBattlefieldCallback self.finishEnterBattlefieldCallback = nil if callback then callback() end end function BattleUnitComp:enterEnterBattlefieldState() if self.isMove then self:playAnimation(SPINE_ANIMATION_NAME.IDLE, true, false) self.positionX = self.baseObject:fastGetLocalPosition() if self.side == BattleConst.SIDE_ATK then self.targetX = -BattleConst.INIT_POS_X self.moveDirection = 1 else self.targetX = BattleConst.INIT_POS_X self.moveDirection = -1 end local time = math.abs(self.targetX - self.positionX)/BattleConst.MOVE_SPEED_ENTER self.battleController:moveBattlefield(time) else self:playAnimation(SPINE_ANIMATION_NAME.BORN, false, false) self.waitTime = self:getAnimationDuration(SPINE_ANIMATION_NAME.BORN) end end function BattleUnitComp:exitIdleState() end function BattleUnitComp:enterIdleState() self:playAnimation(SPINE_ANIMATION_NAME.IDLE, true, false) end function BattleUnitComp:updateIdle(dt) self:updateHurt(dt) end function BattleUnitComp:playHurt() self.hurtTime = 0 if self.currHitDuration == nil then self.currHitDuration = self:getAnimationDuration(SPINE_ANIMATION_NAME.HIT) end self:playAnimation(SPINE_ANIMATION_NAME.HIT, false, false) end function BattleUnitComp:updateHurt(dt) if self.isPlayHurt == 0 then return end if self.isPlayHurt == 1 then self.hurtTime = self.hurtTime + dt if self.hurtTime >= self.currHitDuration then self:playAnimation(SPINE_ANIMATION_NAME.IDLE, true, false) self.isPlayHurt = 0 end end end function BattleUnitComp:enterRecoverHpWaveState() self.recoverHpCount = BattleConst.RECOVER_HP_COUNT self.recoverHpTime = BattleConst.RECOVER_HP_INTERVAL / 2 end function BattleUnitComp:exitRecoverHpWaveState() end function BattleUnitComp:updateRecoverHpWaveState(dt) if self.recoverHpCount <= 0 then return end self.recoverHpTime = self.recoverHpTime - dt if self.recoverHpTime < 0 then self.recoverHpCount = self.recoverHpCount - 1 self.recoverHpTime = BattleConst.RECOVER_HP_INTERVAL local healNum = BattleConst.RECOVER_HP_PERCENT * self.unitEntity:getMaxHp() // DEFAULT_FACTOR self:takeDamageOrCure(self, healNum, EFFECT_TYPE.HEAL, 0) if self.recoverHpCount <= 0 then if self.finishRecoverHpCallback then local callback = self.finishRecoverHpCallback self.finishRecoverHpCallback = nil callback() end end end end function BattleUnitComp:enterWaitState() end function BattleUnitComp:exitWaitState() end function BattleUnitComp:updateWaitState(dt) self.waitTime = self.waitTime - dt if self.waitTime < 0 then self:changeState(self.waitingState) end end function BattleUnitComp:enterAssistingAttackState() self.attackOver = false self.attackTime = 0 self.isMove = false local skill = self.unitEntity:getAssistingSkill() skill:startUse() local attackName = skill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(skill) self:initPosition() end function BattleUnitComp:exitAssistingAttackState() self:hideOutsideScreen() if self.assistingDmgAddition > 0 then local attrName = GConst.MATCH_ATTACK_NAME[self:getMatchType()] self.unitEntity:addAttr(attrName, -self.assistingDmgAddCount, false) self.assistingDmgAddition = 0 self.assistingDmgAddCount = 0 end end function BattleUnitComp:updateAssistingAttackState(dt) self.attackTime = self.attackTime + dt if self.attackTime >= self.currAttackDuration then self.attackOver = true self:onAttackOver() else if self.currAttackKeyTime > 0 and self.attackTime >= self.currAttackKeyTime then -- 到达关键后使用 self.currAttackKeyTime = 0 local skill = self.unitEntity:getAssistingSkill() self:onSkillTakeEffect(skill) end end end function BattleUnitComp:enterSkillAttackState() self.attackOver = false self.attackTime = 0 self.targetX = nil local skill if self.normalSkillCount > 0 then skill = self.unitEntity:getNormalSkill(true) else skill = self.currActiveSkill end skill:startUse() if skill:getMoveType() == BattleConst.SKILL_MOVE_TYPE.MOVE then self.isMove = true self:playAnimation(BattleConst.SPINE_ANIMATION_NAME.MOVE, true, false) self.positionX = self.baseObject:fastGetLocalPosition() if self.side == BattleConst.SIDE_ATK then self.targetX = BattleConst.UNIT_FRONT_POS_X self.moveDirection = 1 else self.targetX = -BattleConst.UNIT_FRONT_POS_X self.moveDirection = -1 end else self.isMove = false self.attackTime = 0 local attackName = skill:getSkillAttackName() if self.normalSkillCount > 0 then self:attackAndSpeedUp() end self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(skill) end end function BattleUnitComp:exitSkillAttackState() end function BattleUnitComp:updateSkillAttack(dt) if self.isMove then local addX = dt*BattleConst.MOVE_SPEED*self.moveDirection self.positionX = self.positionX + addX if (self.moveDirection > 0 and self.positionX >= self.targetX) or (self.moveDirection < 0 and self.positionX <= self.targetX) then self.isMove = false self.positionX = self.targetX if self.attackOver then -- 归位后该下一步了 self:onAttackOver() else -- 到位置该攻击了 self:doNextAttack() end end self.baseObject:setLocalPosition(self.positionX, 0, 0) return end self.attackTime = self.attackTime + dt if self.attackTime >= self.currAttackDuration then self.attackOver = true if self.normalSkillCount > 0 then self.normalSkillCount = self.normalSkillCount - 1 if self.normalSkillCount <= 0 then if self.currActiveSkill == nil then if self.targetX then -- 移动过,准备归位 self:moveBackToInitPosition() else self:onAttackOver() end return else self.currActiveSkill:startUse() self:doNextSkillAttack() end else -- 继续普攻 self:doNextNormalAttack() end else local currActiveSkill = nil local skillCanUseTimes = self.currActiveSkill:getSkillCanUseTimes() if skillCanUseTimes and skillCanUseTimes > 0 then -- 当前技能可以多次使用 currActiveSkill = self.currActiveSkill elseif self.activeSkillIndex then self.activeSkillIndex = self.activeSkillIndex + 1 local activeSkillCount = self.unitEntity:getActiveSkillCount() while self.activeSkillIndex <= activeSkillCount do currActiveSkill = self.unitEntity:getAvailableActiveSkill(self.activeSkillIndex) if currActiveSkill then currActiveSkill:startUse() break end self.activeSkillIndex = self.activeSkillIndex + 1 end end if currActiveSkill == nil then if self.targetX then -- 移动过,准备归位 self:moveBackToInitPosition() else self:onAttackOver() end return else self.currActiveSkill = currActiveSkill self:doNextSkillAttack() end end else if self.currAttackKeyTime > 0 and self.attackTime >= self.currAttackKeyTime then -- 到达关键后使用 self.currAttackKeyTime = 0 if self.normalSkillCount > 0 then if self.normalSkillCount == 1 and self.currActiveSkill == nil then -- 最后一次攻击 self.team:setCentralizedAttack(false) end local skill = self.unitEntity:getNormalSkill() self:onSkillTakeEffect(skill) if self.normalSkillCount == 1 and self.currActiveSkill == nil then -- 最后一次攻击 self.battleController:setIsPauseHpProgress(false) end else local isHaveNextAttack = self:getIsHaveNextAvailableActiveSkill() if not isHaveNextAttack then self.team:setCentralizedAttack(false) end self:onSkillTakeEffect(self.currActiveSkill) if not isHaveNextAttack then self.battleController:setIsPauseHpProgress(false) end end end end end function BattleUnitComp:getIsHaveNextAvailableActiveSkill() local skillCanUseTimes = self.currActiveSkill:getSkillCanUseTimes() -- 当前技能可以多次使用,onSkillTakeEffect才会次数减1,所以这次大于1的时候才说明可以多次使用 if skillCanUseTimes and skillCanUseTimes > 1 then return true elseif self.activeSkillIndex then local currActiveSkill = nil local activeSkillIndex = self.activeSkillIndex + 1 local activeSkillCount = self.unitEntity:getActiveSkillCount() while activeSkillIndex <= activeSkillCount do currActiveSkill = self.unitEntity:getAvailableActiveSkill(activeSkillIndex) if currActiveSkill then return true end activeSkillIndex = activeSkillIndex + 1 end end return false end function BattleUnitComp:moveBackToInitPosition() self.isMove = true self:playAnimation(BattleConst.SPINE_ANIMATION_NAME.MOVE, true, false) self.positionX = self.baseObject:fastGetLocalPosition() if self.side == BattleConst.SIDE_ATK then self.targetX = -BattleConst.INIT_POS_X self.moveDirection = -1 else self.targetX = BattleConst.INIT_POS_X self.moveDirection = 1 end end function BattleUnitComp:onAttackOver() self:changeState(UNIT_STATE.IDLE) local callback = self.actionOverCallback self.actionOverCallback = nil if callback then callback() end end function BattleUnitComp:attackAndSpeedUp() self.attackCount = self.attackCount + 1 if self.attackCount == 3 then DataManager.BattleData:addTimeSpeed() elseif self.attackCount == 5 then DataManager.BattleData:addTimeSpeed() end end function BattleUnitComp:doNextSkillAttack() self.attackTime = 0 local attackName = self.currActiveSkill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(self.currActiveSkill) DataManager.BattleData:resetTimeSpeed() end function BattleUnitComp:doNextNormalAttack() self.attackTime = 0 local skill = self.unitEntity:getNormalSkill(true) skill:startUse() local attackName = skill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(skill) self:attackAndSpeedUp() end function BattleUnitComp:doNextAttack() self.attackTime = 0 local attackName = nil if self.normalSkillCount > 0 then local skill = self.unitEntity:getNormalSkill(true) skill:startUse() attackName = skill:getSkillAttackName() if attackName then self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(skill) self:attackAndSpeedUp() else self:moveBackToInitPosition() end elseif self.currActiveSkill then self.currActiveSkill:startUse() attackName = self.currActiveSkill:getSkillAttackName() if attackName then self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(self.currActiveSkill) DataManager.BattleData:resetTimeSpeed() else self:moveBackToInitPosition() end else self:moveBackToInitPosition() end end function BattleUnitComp:enterNormalAttackState() self.attackOver = false self.attackTime = 0 local skill = self.unitEntity:getNormalSkill(true) skill:startUse() if skill:getMoveType() == BattleConst.SKILL_MOVE_TYPE.MOVE then self.isMove = true self:playAnimation(BattleConst.SPINE_ANIMATION_NAME.MOVE, true, false) self.positionX = self.baseObject:fastGetLocalPosition() if self.side == BattleConst.SIDE_ATK then self.targetX = BattleConst.UNIT_FRONT_POS_X self.moveDirection = 1 else self.targetX = -BattleConst.UNIT_FRONT_POS_X self.moveDirection = -1 end else self.isMove = false self.attackTime = 0 local attackName = skill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getAnimationKeyFrameTime(attackName) self:playAnimation(attackName, false, false) self:playSkillFx(skill) self:attackAndSpeedUp() end end function BattleUnitComp:exitNormalAttackState() end function BattleUnitComp:updateNormalAttack(dt) if self.isMove then local addX = dt*BattleConst.MOVE_SPEED*self.moveDirection self.positionX = self.positionX + addX if (self.moveDirection > 0 and self.positionX >= self.targetX) or (self.moveDirection < 0 and self.positionX <= self.targetX) then self.isMove = false self.positionX = self.targetX if self.attackOver then -- 归位后该下一步了 self:onAttackOver() else -- 到位置该攻击了 self:doNextNormalAttack() end end self.baseObject:setLocalPosition(self.positionX, 0, 0) return end self.attackTime = self.attackTime + dt if self.attackTime >= self.currAttackDuration then self.attackOver = true self.normalSkillCount = self.normalSkillCount - 1 if self.normalSkillCount <= 0 then local skill = self.unitEntity:getNormalSkill() if skill:getMoveType() == BattleConst.SKILL_MOVE_TYPE.MOVE then self:moveBackToInitPosition() else self:onAttackOver() end return else -- 继续攻击 self:doNextNormalAttack() end else if self.currAttackKeyTime > 0 and self.attackTime >= self.currAttackKeyTime then -- 到达关键后使用 self.currAttackKeyTime = 0 if self.normalSkillCount == 1 then -- 如果是最后一次攻击,那么敌人受到这次攻击可以开始嗝屁了 self.team:setCentralizedAttack(false) end local skill = self.unitEntity:getNormalSkill() self:onSkillTakeEffect(skill) if self.normalSkillCount == 1 then -- 如果是最后一次攻击,那么可以开始跑血条了 self.battleController:setIsPauseHpProgress(false) end end end end function BattleUnitComp:addMaxHp(percent) self.unitEntity:addMaxHp(percent) local hpPercent = self.unitEntity:getHpPercent() local hp = self.unitEntity:getHp() self.battleController:refreshHp(self.side, hp, hpPercent) end function BattleUnitComp:addBuff(buffEffect) self.team:addBuff(buffEffect) end function BattleUnitComp:updateBuffState(buff, num) self.team:updateBuffState(buff, num) end function BattleUnitComp:removeAllBuff() self.team:removeAllBuff() end function BattleUnitComp:onSkillTakeEffect(skill) skill:endUse() if skill:getIsEliminateType() then self.battleController:generateGridType(skill:getEliminateSkillParameter(), self.baseObject:getTransform().position) end local effectList = skill:getEffectList() if effectList == nil then return end if #effectList == 0 then return end local targetType = skill:getTargetType() local target if targetType == 1 then -- 自己 target = self else target = self.battleController:getOtherSideMainUnit(self.side) if skill:getIsNormalType() then -- 普攻要计算一下格挡 local block = target.unitEntity:getBlock() if block > 0 then if BattleHelper:random(1, DEFAULT_FACTOR) <= block then -- 格挡成功 return end end end end local succ = false for k, effect in ipairs(effectList) do if self:takeEffect(effect, target) then succ = true end end if succ then if skill:getIsNormalType() then -- 普攻攻击成功的话 self:checkPassiveEvent(PASSIVE_EVENT.USE_NORMAL_SKILL, target) elseif skill:getIsActiveType() then self:checkPassiveEvent(PASSIVE_EVENT.ACTIVE_SKILL_HIT, target) end local shakeType = skill:getShakeType() if shakeType then local shakeTime = skill:getShakeTime() or 0 if shakeTime > 0 then self.battleController:shakeScreen(shakeType, shakeTime/TIME_FACTOR) end end local soundHit = skill:getSoundHit() if soundHit then BattleHelper:playSkillSound(soundHit, 0) end end end function BattleUnitComp:takeEffect(buff, target) local ratio = buff:getRatio() if ratio < DEFAULT_FACTOR then if BattleHelper:random(1, DEFAULT_FACTOR) > ratio then -- 没有通过命中概率 return false end end local round = buff:getRound() local buffEffect if round > 0 then buffEffect = BattleHelper:getBuffEffect() buffEffect.buff = buff buffEffect.result = nil buffEffect.round = round buffEffect.target = target buffEffect.sender = self target:addBuff(buffEffect) end local func = BattleBuffHandle.takeBuffEffect[buff:getBuffType()] if func then local result = func(self, buff, target, buffEffect) if buffEffect then buffEffect.result = result end local success = result ~= nil if success then local fxList = buff:getFxGet() if fxList then for _, v in ipairs(fxList) do target:playFx(v, 0) end end end return success end return false end function BattleUnitComp:removeEffect(buff, target) local round = buff:getRound() if round > 0 then return end local func = BattleBuffHandle.removeEffect[buff:getBuffType()] if func then func(self, target, buff) end end function BattleUnitComp:takeDamageOrCure(atker, num, effectType, effectStatus) if self:getIsClear() then return 0 end if self.currState == UNIT_STATE.DEAD then return 0 end if num == 0 then return 0 end atker.unitEntity:addDamageCount(num) local shieldHpBefore = self.unitEntity:getShieldHp() local hpRealReduce = self.unitEntity:takeDamageOrCure(num) if hpRealReduce < 0 and self.side == BattleConst.SIDE_DEF then -- 实际掉血了 local exp = self.unitEntity:getExp() if exp > 0 then local expTime = atker.unitEntity:getExpTime() local addExp = math.floor(exp/self.unitEntity:getMaxHp()*-hpRealReduce) if expTime > 0 then addExp = addExp * (DEFAULT_FACTOR + expTime) // DEFAULT_FACTOR end self.battleController:addBattleExp(addExp) end end local shieldHpDiff = self.unitEntity:getShieldHp() - shieldHpBefore if shieldHpDiff < 0 then -- 说明护盾减少了 self.team:handleShield(shieldHpDiff, self) end local hp = self.unitEntity:getHp() local x, y = self.baseObject:fastGetLocalPosition() local damage = num if num < 0 then -- 伤害 local delayTime = 0 if shieldHpDiff < 0 then damage = damage - shieldHpDiff delayTime = BattleConst.EFFECT_NUMBER_DELAY if self.side == BattleConst.SIDE_ATK then self:showEffectNumber(BattleConst.EFFECT_COLOR_BLUE, BattleConst.EFFECT_TYPE_MOVE_L, shieldHpDiff, x, y, 0) else self:showEffectNumber(BattleConst.EFFECT_COLOR_BLUE, BattleConst.EFFECT_TYPE_MOVE_R, shieldHpDiff, x, y, 0) end end if damage < 0 then if effectStatus == HURT_STATE_CRIT then self:showEffectNumber(BattleConst.EFFECT_COLOR_RED, BattleConst.EFFECT_TYPE_CRIT, "c" .. damage, x, y, delayTime) else if self.side == BattleConst.SIDE_ATK then self:showEffectNumber(BattleConst.EFFECT_COLOR_RED, BattleConst.EFFECT_TYPE_MOVE_L, damage, x, y, delayTime) else self:showEffectNumber(BattleConst.EFFECT_COLOR_RED, BattleConst.EFFECT_TYPE_MOVE_R, damage, x, y, delayTime) end end end if hp > 0 and self.unitEntity:getShieldRebound() then -- 伤害反弹 atker:takeDamageOrCure(self, num*self.unitEntity:getShieldRebound() // DEFAULT_FACTOR, EFFECT_TYPE.DIRECT, 0) end elseif num > 0 then -- 治疗 self:showEffectNumber(BattleConst.EFFECT_COLOR_GREEN, BattleConst.EFFECT_TYPE_BUFF, "+" .. num, x, y, 0) end local hpPercent = self.unitEntity:getHpPercent() self.battleController:refreshHp(self.side, hp, hpPercent) if atker:getIsCentralizedAttack() then if damage < 0 and self.currState == UNIT_STATE.IDLE then self:playHurt() end else if self.unitEntity:getIsDead() then self:changeState(UNIT_STATE.DEAD) elseif damage < 0 and self.currState == UNIT_STATE.IDLE then self:playHurt() end end if hp > 0 then self.team:checkPassiveEvent(PASSIVE_EVENT.HP_LOWER_THAN, atker, hpPercent) end end function BattleUnitComp:showEffectNumber(colorType, effectType, num, x, y, delayTime) local addY = BattleConst.MIN_NODE_HEIGHT[self.unitEntity:getBody()] or BattleConst.MIN_NODE_HEIGHT_DEFAULT self.battleController:showEffectNumber(colorType, effectType, num, x, y + addY, delayTime) end function BattleUnitComp:playDead(callback) self.deadOverCallback = callback if self:getIsClear() then self.deadOverCallback = nil if callback then callback() end return end if self.currState ~= UNIT_STATE.DEAD and not self:changeState(UNIT_STATE.DEAD) then self.deadOverCallback = nil if callback then callback() end end end function BattleUnitComp:recoverHpOnWaveOver(callback) self.finishRecoverHpCallback = callback if not self:changeState(UNIT_STATE.RECOVER_HP_WAVE) then self.finishRecoverHpCallback = nil callback() end end function BattleUnitComp:playEnterBattlefield(isBoss, callback) self.finishEnterBattlefieldCallback = callback if isBoss then self.isMove = true self:hideOutsideScreen() if not self:changeState(UNIT_STATE.ENTER_BATTLEFIELD) then self.finishEnterBattlefieldCallback = nil self.isMove = false self:initPosition() self:changeState(UNIT_STATE.IDLE) callback() end else self.isMove = false self:initPosition() if not self:changeState(UNIT_STATE.ENTER_BATTLEFIELD) then self.finishEnterBattlefieldCallback = nil self:changeState(UNIT_STATE.IDLE) callback() end end end function BattleUnitComp:onRoundEnd() self.unitEntity:onRoundEnd() end function BattleUnitComp:playSkillFx(skill) self:playSkillSelfFx(skill) self:playSkillTargetFx(skill) end function BattleUnitComp:playSkillSelfFx(skill) local fxId = skill:getFxSelf() if fxId == nil then return end local delay = skill:getFxSelfDelay() self:playFx(fxId, delay) end function BattleUnitComp:playSkillTargetFx(skill) local fxId = skill:getFxTarget() if fxId == nil then return end local delay = skill:getFxTargetDelay() local targetType = skill:getTargetType() if targetType == 1 then -- 自己 self:playFx(fxId, delay) else local target = self.battleController:getOtherSideMainUnit(self.side) target:playFx(fxId, delay) end end function BattleUnitComp:playFx(fxId, delay) if delay and delay > 0 then if self.waitPlayFxList == nil then self.waitPlayFxList = {} self.waitPlayFxCount = 0 end self.waitPlayFxCount = self.waitPlayFxCount + 1 local waitPlayFxObj = self.waitPlayFxList[self.waitPlayFxCount] if waitPlayFxObj == nil then waitPlayFxObj = {} self.waitPlayFxList[self.waitPlayFxCount] = waitPlayFxObj end waitPlayFxObj.time = delay / TIME_FACTOR waitPlayFxObj.fxId = fxId waitPlayFxObj.sender = self return end local fxInfo = BattleHelper:getFxConfig()[fxId] if fxInfo then self:getEffectAndPlay(fxInfo) end end function BattleUnitComp:getEffectAndPlay(fxInfo, isLoop) local bindNode = fxInfo.bind local inScene = fxInfo.in_scene == 1 local fxScale = fxInfo.scale or 1 BattleHelper:getEffectAsync(fxInfo.res, self.battleController:getFxNode(), function(effectObj) if isLoop and self.team:getLoopFxResCount(fxInfo.res) <= 0 then BattleHelper:recycleEffect(effectObj) return end if inScene then if bindNode then local boneX, boneY, boneZ = self.baseObject:fastGetBonePosition(bindNode) effectObj:setLocalPosition(boneX, boneY, boneZ) else effectObj:setLocalPosition(0, 0, 0) end else if bindNode then local boneFollower = BattleHelper:addSpineBoneFollowerGraphic(effectObj) boneFollower.SkeletonGraphic = self.baseObject:getSkeletonGraphic() boneFollower:SetBone(bindNode) else effectObj:setLocalPosition(0, 0, 0) end end if fxInfo.follow_direction then if self.side == BattleConst.SIDE_ATK then if effectObj:getIsEulerAnglesFlip() then effectObj:setLocalEulerAngles(0, 0, 0) end effectObj:setLocalScale(fxScale, fxScale, fxScale) else if effectObj:getIsEulerAnglesFlip() then effectObj:setLocalEulerAngles(0, 180, 0) effectObj:setLocalScale(fxScale, fxScale, fxScale) else effectObj:setLocalScale(-fxScale, fxScale, fxScale) end end else if effectObj:getIsEulerAnglesFlip() then effectObj:setLocalEulerAngles(0, 0, 0) end effectObj:setLocalScale(fxScale, fxScale, fxScale) end local baseOrder = BattleHelper:getBaseOrder() effectObj:setSortingOrder(baseOrder, 10) effectObj:play() if isLoop then if self.loopFxMap[fxInfo.res] then BattleHelper:recycleEffect(effectObj) else self.loopFxMap[fxInfo.res] = effectObj end else BattleHelper:performDurationDelay(effectObj:getDuration(), function() BattleHelper:recycleEffect(effectObj) end) end end) end function BattleUnitComp:removeEffect(res) local effect = self.loopFxMap[res] if effect then self.loopFxMap[res] = nil BattleHelper:recycleEffect(effect) end end function BattleUnitComp:tick(dt) if self.isClear then return end self:checkFx(dt) if self.currState == UNIT_STATE.DEAD then self:updateDead(dt) return end if self.currState == UNIT_STATE.IDLE then self:updateIdle(dt) return end if self.currState == UNIT_STATE.NORMAL_ATTACK then self:updateNormalAttack(dt) elseif self.currState == UNIT_STATE.SKILL_ATTACK then self:updateSkillAttack(dt) elseif self.currState == UNIT_STATE.ASSISTING_ATTACK then self:updateAssistingAttackState(dt) elseif self.currState == UNIT_STATE.ENTER_BATTLEFIELD then self:updateEnterBattlefieldState(dt) elseif self.currState == UNIT_STATE.SWITCH_IN then self:updateSwitchInState(dt) elseif self.currState == UNIT_STATE.SWITCH_OUT then self:updateSwitchOutState(dt) elseif self.currState == UNIT_STATE.WAIT then self:updateWaitState(dt) elseif self.currState == UNIT_STATE.RECOVER_HP_WAVE then self:updateRecoverHpWaveState(dt) end end function BattleUnitComp:checkFx(dt) if self.waitPlayFxCount == nil then return end if self.waitPlayFxCount <= 0 then return end for i = self.waitPlayFxCount, 1, -1 do local fxObj = self.waitPlayFxList[i] fxObj.time = fxObj.time - dt if fxObj.time <= 0 then -- 该播了 self:playFx(fxObj.fxId, 0) local tailFxObj = self.waitPlayFxList[self.waitPlayFxCount] fxObj.time = tailFxObj.time fxObj.fxId = tailFxObj.fxId fxObj.sender = tailFxObj.sender self.waitPlayFxCount = self.waitPlayFxCount - 1 end end end function BattleUnitComp:checkPassiveEvent(eventId, targetComp, ...) if self.passiveSkills == nil then return end local skills = self.passiveSkills[eventId] if skills and #skills > 0 then local func = BattlePassive.checkTrigger[eventId] if func then for _, skill in ipairs(skills) do local count = func(self, skill, targetComp, ...) if count > 0 and skill:getIsAvailable() then for i = 1, count do self:usePassiveSkill(skill) end elseif count < 0 then count = -count for i = 1, count do self:cancelPassiveSkillEffect(skill) end end end end end end function BattleUnitComp:usePassiveSkill(skill) self:onSkillTakeEffect(skill) end function BattleUnitComp:cancelPassiveSkillEffect(skill) local effectList = skill:getEffectList() if effectList == nil then return end local targetType = skill:getTargetType() local target if targetType == 1 then -- 自己 target = self else target = self.battleController:getOtherSideMainUnit(self.side) end for k, effect in ipairs(effectList) do self:removeEffect(effect, target) end end function BattleUnitComp:getIsClear() return self.isClear end function BattleUnitComp:clear() if self.isClear then return end self.isClear = true if self.waitPlayFxList then for i = 1, #self.waitPlayFxList do table.remove(self.waitPlayFxList) end self.waitPlayFxCount = 0 end end function BattleUnitComp:recycle() for k, v in pairs(self.loopFxMap) do BattleHelper:recycleEffect(v) self.loopFxMap[k] = nil end BattleHelper:recycleBattleHeroModel(self.modelId, self.baseObject) end return BattleUnitComp