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 BATTLE_BOARD_SKILL_HANDLE = require "app/module/battle/skill/battle_board_skill_handle" local BATTLE_SKILL_CONDITION_HANDLE = require "app/module/battle/helper/battle_skill_condition_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 local HURT_ANI_NAME_LIST = {SPINE_ANIMATION_NAME.HIT, SPINE_ANIMATION_NAME.HIT_2} 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:getIsLethargy() return self.unitEntity:getIsLethargy() end function BattleUnitComp:getIsStun() return self.unitEntity:getIsStun() end function BattleUnitComp:getIsFrozen() return self.unitEntity:getIsFrozen() end function BattleUnitComp:getActiveSkillLimit() return self.unitEntity:getActiveSkillLimit() 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.skillSlowDownTime = nil -- 开始攻击后,x秒后开始子弹时间 self.skillSlowDownDuration = 0 -- 子弹时间持续的时间 self.skillSlowDownScale = 1 -- 子弹时间减慢的倍率 self.currAttackDuration = 0 self.currAttackKeyTime = 0 self.currAttackBlockIndex = 0 -- 多段伤害索引 self.validEffectIdx = {} self.switchTime = 0 self.isPlayingSubAni = false self.playSubAniDuration = {} 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() self:initHitAniInfo() end function BattleUnitComp:initHitAniInfo() self.hurtAniNameList = {} self.hurtAniNameCount = 0 for _, name in ipairs(HURT_ANI_NAME_LIST) do local duration = self:getAnimationDuration(name) if duration > 0 then table.insert(self.hurtAniNameList, name) self.hurtAniNameCount = self.hurtAniNameCount + 1 end end 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 == self.curHurtName or name == SPINE_ANIMATION_NAME.BLOCK then self.isPlayingSubAni = true 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, index) index = index or 1 local timeList = self.attackKeyFrameTimeMap[animationName] if timeList == nil then timeList = self.baseObject:getAnimationKeyFrameTimes(animationName) if not timeList[1] then table.insert(timeList, 0.3) end self.attackKeyFrameTimeMap[animationName] = timeList end return timeList[index] or 0.3 end function BattleUnitComp:getKeyFrameTime(skill, blockIndex, animationName) if not skill then return 0 end self.currAttackBlockIndex = blockIndex local time = 0 local blockList = skill:getEffectBlock() if blockList[blockIndex] then local beforeIndex = blockIndex - 1 self.validEffectIdx[1] = blockList[beforeIndex] or 0 self.validEffectIdx[2] = blockList[blockIndex] or 1 time = skill:getEffectBlockTime()[blockIndex] if not time then time = self:getAnimationKeyFrameTime(animationName, blockIndex) end end return time end function BattleUnitComp:getIsCentralizedAttack() return self.team:getCentralizedAttack() end function BattleUnitComp:getIsFinalBlock() return self.team:getIsFinalBlock() end function BattleUnitComp:getCurAttackActionState() return self.curAttackActionState end function BattleUnitComp:getHurtComboTag() return self.team:getHurtComboTag(self:getMatchType()) end function BattleUnitComp:beforeAttack(actionState) self.team:setCentralizedAttack(true) self.team:setIsFinalBlock(false) self.battleController:setIsPauseHpProgress(true) self.curAttackActionState = actionState if actionState == BattleConst.ATTACK_ACTION_STATE.NORMAL then self:checkPassiveEvent(PASSIVE_EVENT.ON_UNI_ATTACK_START, self) end 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.unitEntity:addActiveSkillReleaseCount(1) self.actionOverCallback = callback self.activeSkillIndex = nil self.currActiveSkill = self.unitEntity:getAvailableActiveSkill(index) if self:getActiveSkillLimit() then self.currActiveSkill = nil end 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 count <= 0 then self:checkPassiveEvent(PASSIVE_EVENT.ON_ACTIVE_SKILL_BEFORE, self) end 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() if self:getActiveSkillLimit() then activeSkillCount = 0 end 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, effectType, callback) self.actionOverCallback = callback self.normalSkillCount = count if effectType == BattleConst.EFFECT_TYPE.DIRECT then self.normalSkillCount = self.normalSkillCount + self.unitEntity:getNormalAttackAddCount() end 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 == UNIT_STATE.DEAD then -- 死亡后只能去死亡状态 if state ~= UNIT_STATE.DEAD then return false end end -- 进入目标状态 if state == UNIT_STATE.IDLE then -- idle为默认状态,其状态下判定特殊状态 if self:getIsFrozen() then -- 有冰冻buff state = UNIT_STATE.FROZEN elseif self:getIsLethargy() or self:getIsStun() then state = UNIT_STATE.VERITGO end end if self.currState == state and not self:repeatCurrState() then return false 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() elseif self.currState == UNIT_STATE.FROZEN then self:exitFrozenState() elseif self.currState == UNIT_STATE.VERITGO then self:exitVeritgoState() 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.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() elseif state == UNIT_STATE.FROZEN then self:enterFrozenState() elseif state == UNIT_STATE.VERITGO then self:enterVeritgoState() 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 self:checkPassiveEvent(PASSIVE_EVENT.ON_ACTIVE_SKILL_BEFORE, self) 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:refreshHpSkin() self:playAnimation(SPINE_ANIMATION_NAME.IDLE, true, false) end function BattleUnitComp:updateIdle(dt) self:updateIdleSubAni(dt, SPINE_ANIMATION_NAME.IDLE) end function BattleUnitComp:updateIdleSubAni(...) if not self.currAnimationName or not self.isPlayingSubAni then return end if self.currAnimationName == self.curHurtName then self:updateHurt(...) elseif self.currAnimationName == SPINE_ANIMATION_NAME.BLOCK then self:updateBlock(...) end end function BattleUnitComp:playHurt() if self.currState == UNIT_STATE.IDLE or self.currState == UNIT_STATE.VERITGO then if self.hurtAniNameCount <= 0 then return end local name = self.hurtAniNameList[math.random(1, self.hurtAniNameCount)] self.playSubAniTime = 0 self.curHurtName = name if self.playSubAniDuration[name] == nil then self.playSubAniDuration[name] = self:getAnimationDuration(name) end self:playAnimation(name, false, false) end end function BattleUnitComp:updateHurt(dt, overAniName) self.playSubAniTime = self.playSubAniTime + dt if self.playSubAniTime >= self.playSubAniDuration[self.curHurtName or SPINE_ANIMATION_NAME.HIT] then self:playAnimation(overAniName, true, false) self.isPlayingSubAni = false end end function BattleUnitComp:playBlock() if self.currState == UNIT_STATE.IDLE then local name = SPINE_ANIMATION_NAME.BLOCK self.playSubAniTime = 0 if self.playSubAniDuration[name] == nil then self.playSubAniDuration[name] = self:getAnimationDuration(name) end self:playAnimation(name, false, false) end local direction = BattleConst.EFFECT_TYPE_MOVE_R local x, y = self.team:getMainUnitLocalPosition(self) if self.side == BattleConst.SIDE_ATK then direction = BattleConst.EFFECT_TYPE_MOVE_L end self:showEffectNumber(BattleConst.EFFECT_COLOR_SPECIAL, direction, I18N:getGlobalText(I18N.GlobalConst.BLOCK_DESC), x, y, 0) end function BattleUnitComp:updateBlock(dt, overAniName) self.playSubAniTime = self.playSubAniTime + dt if self.playSubAniTime >= self.playSubAniDuration[SPINE_ANIMATION_NAME.BLOCK] then self:playAnimation(overAniName, true, false) self.isPlayingSubAni = false end end function BattleUnitComp:exitFrozenState() end function BattleUnitComp:enterFrozenState() self:playAnimation(SPINE_ANIMATION_NAME.FROZEN, true, false) end function BattleUnitComp:updateFrozen(dt) self:updateIdleSubAni(dt, SPINE_ANIMATION_NAME.FROZEN) end function BattleUnitComp:exitVeritgoState() end function BattleUnitComp:enterVeritgoState() self:playAnimation(SPINE_ANIMATION_NAME.VERTIGO, true, false) end function BattleUnitComp:updateVeritgo(dt) self:updateIdleSubAni(dt, SPINE_ANIMATION_NAME.VERTIGO) 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 if healNum < 0 then -- 治疗效果不能为负数 healNum = 0 end self:takeDamageOrCure(self, healNum, EFFECT_TYPE.HEAL, 0, BattleConst.SPECIAL_DAMAGE_OR_CURE_TYPE.ROUND_BEGIN_HEAL) if self.recoverHpCount <= 0 then self:changeState(UNIT_STATE.IDLE) 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.skillSlowDownTime = nil self.isMove = false local skill = self.unitEntity:getAssistingSkill() skill:startUse() local attackName = skill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(skill, 1, attackName) self:playSkillSound(skill:getSkillSound(), skill:getSoundDelay()) 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 -- 到达关键后使用 local skill = self.unitEntity:getAssistingSkill() local isFinalBlock = skill and skill:isFinalBlock(self.currAttackBlockIndex) self:onSkillTakeEffect(skill, isFinalBlock, self.validEffectIdx) if skill then local attackName = skill:getSkillAttackName() self.currAttackKeyTime = self:getKeyFrameTime(skill, self.currAttackBlockIndex + 1, attackName) else self.currAttackKeyTime = 0 end 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) self.skillSlowDownTime = nil else skill = self.currActiveSkill local slowDownBulletTimeParams = skill:getSlowDownBulletTimeParams() if slowDownBulletTimeParams and #slowDownBulletTimeParams == 3 then self.skillSlowDownTime = slowDownBulletTimeParams[1] / TIME_FACTOR self.skillSlowDownScale = slowDownBulletTimeParams[2] / DEFAULT_FACTOR self.skillSlowDownDuration = slowDownBulletTimeParams[3] / TIME_FACTOR else self.skillSlowDownTime = nil end 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.INIT_POS_X + skill:getMoveTypeParams() self.moveDirection = 1 else self.targetX = BattleConst.INIT_POS_X - skill:getMoveTypeParams() self.moveDirection = -1 end else self.isMove = false local attackName = skill:getSkillAttackName() if self.normalSkillCount > 0 then self:attackAndSpeedUp() else if not attackName then -- 没有攻击动画 self.currAttackDuration = 0 self.currAttackKeyTime = 0 self.battleController:resetTimeSpeed() local isFinalBlock = true local isHaveNextAttack = self:getIsHaveNextAvailableActiveSkill() if not isHaveNextAttack and isFinalBlock then self.team:setCentralizedAttack(false) self.team:setIsFinalBlock(isFinalBlock) end self:onSkillTakeEffect(skill, isFinalBlock) if not isHaveNextAttack and isFinalBlock then self.battleController:setIsPauseHpProgress(false) end return end end self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(skill, 1, attackName) self:playSkillSound(skill:getSkillSound(), skill:getSoundDelay()) self:playAnimation(attackName, false, false) self:playSkillFx(skill) end end function BattleUnitComp:checkSkillSlowDown(dt) if self.skillSlowDownTime == nil then return end self.skillSlowDownTime = self.skillSlowDownTime - dt if self.skillSlowDownTime >= 0 then return end self.skillSlowDownTime = nil self.battleController:setSkillSlowDown(self.skillSlowDownScale, self.skillSlowDownDuration) 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:checkSkillSlowDown(dt) 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:checkPassiveEvent(PASSIVE_EVENT.ON_ACTIVE_SKILL_BEFORE, self) self.currActiveSkill:startUse() self:doNextSkillAttack() end else -- 继续普攻 self:doNextNormalAttack() end else if self.currAttackDuration <= 0 and self.currActiveSkill then -- 当前动画异常 没有进入onSkillTakeEffect,不会攻击次数减一,所以手动调用一次 self.currActiveSkill:endUse() end 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 local target = self.battleController:getOtherSideMainUnit(self.side) if target and target.unitEntity:getIsDead() then self:checkPassiveEvent(PASSIVE_EVENT.ON_DEAD_BY_SKILL, self) end return else self.currActiveSkill = currActiveSkill self:doNextSkillAttack() end end else if self.currAttackKeyTime > 0 and self.attackTime >= self.currAttackKeyTime then -- 到达关键后使用 if self.normalSkillCount > 0 then local skill = self.unitEntity:getNormalSkill() local isFinalBlock = skill and skill:isFinalBlock(self.currAttackBlockIndex) if self.normalSkillCount == 1 and self.currActiveSkill == nil and isFinalBlock then -- 最后一次攻击 self.team:setCentralizedAttack(false) end self:onSkillTakeEffect(skill, isFinalBlock, self.validEffectIdx) if self.normalSkillCount == 1 and self.currActiveSkill == nil and isFinalBlock then -- 最后一次攻击 self.battleController:setIsPauseHpProgress(false) end if skill then local attackName = skill:getSkillAttackName() self.currAttackKeyTime = self:getKeyFrameTime(skill, self.currAttackBlockIndex + 1, attackName) else self.currAttackKeyTime = 0 end else local isFinalBlock = self.currActiveSkill:isFinalBlock(self.currAttackBlockIndex) local isHaveNextAttack = self:getIsHaveNextAvailableActiveSkill() if not isHaveNextAttack and isFinalBlock then self.team:setCentralizedAttack(false) self.team:setIsFinalBlock(isFinalBlock) end self:onSkillTakeEffect(self.currActiveSkill, isFinalBlock, self.validEffectIdx) if not isHaveNextAttack and isFinalBlock then self.battleController:setIsPauseHpProgress(false) end local attackName = self.currActiveSkill:getSkillAttackName() self.currAttackKeyTime = self:getKeyFrameTime(self.currActiveSkill, self.currAttackBlockIndex + 1, attackName) 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 self.battleController:addTimeSpeed() elseif self.attackCount == 5 then self.battleController:addTimeSpeed() end end function BattleUnitComp:doNextSkillAttack() self.attackTime = 0 local slowDownBulletTimeParams = self.currActiveSkill:getSlowDownBulletTimeParams() if slowDownBulletTimeParams and #slowDownBulletTimeParams == 3 then self.skillSlowDownTime = slowDownBulletTimeParams[1] / TIME_FACTOR self.skillSlowDownScale = slowDownBulletTimeParams[2] / DEFAULT_FACTOR self.skillSlowDownDuration = slowDownBulletTimeParams[3] / TIME_FACTOR else self.skillSlowDownTime = nil end local attackName = self.currActiveSkill:getSkillAttackName() if not attackName then -- 没有攻击动画 self.currAttackDuration = 0 self.currAttackKeyTime = 0 self.battleController:resetTimeSpeed() local isFinalBlock = true local isHaveNextAttack = self:getIsHaveNextAvailableActiveSkill() if not isHaveNextAttack and isFinalBlock then self.team:setCentralizedAttack(false) self.team:setIsFinalBlock(isFinalBlock) end self:onSkillTakeEffect(self.currActiveSkill, isFinalBlock) if not isHaveNextAttack and isFinalBlock then self.battleController:setIsPauseHpProgress(false) end return end self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(self.currActiveSkill, 1, attackName) self:playSkillSound(self.currActiveSkill:getSkillSound(), self.currActiveSkill:getSoundDelay()) self:playAnimation(attackName, false, false) self:playSkillFx(self.currActiveSkill) self.battleController:resetTimeSpeed() end function BattleUnitComp:doNextNormalAttack() self.attackTime = 0 self.skillSlowDownTime = nil local skill = self.unitEntity:getNormalSkill(true) skill:startUse() local attackName = skill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(skill, 1, attackName) self:playSkillSound(skill:getSkillSound(), skill:getSoundDelay()) 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 self.skillSlowDownTime = nil local skill = self.unitEntity:getNormalSkill(true) skill:startUse() attackName = skill:getSkillAttackName() if attackName then self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(skill, 1, attackName) self:playSkillSound(skill:getSkillSound(), skill:getSoundDelay()) self:playAnimation(attackName, false, false) self:playSkillFx(skill) self:attackAndSpeedUp() else self:moveBackToInitPosition() end elseif self.currActiveSkill then local slowDownBulletTimeParams = self.currActiveSkill:getSlowDownBulletTimeParams() if slowDownBulletTimeParams and #slowDownBulletTimeParams == 3 then self.skillSlowDownTime = slowDownBulletTimeParams[1] / TIME_FACTOR self.skillSlowDownScale = slowDownBulletTimeParams[2] / DEFAULT_FACTOR self.skillSlowDownDuration = slowDownBulletTimeParams[3] / TIME_FACTOR else self.skillSlowDownTime = nil end self.currActiveSkill:startUse() attackName = self.currActiveSkill:getSkillAttackName() if attackName then self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(self.currActiveSkill, 1, attackName) self:playSkillSound(self.currActiveSkill:getSkillSound(), self.currActiveSkill:getSoundDelay()) self:playAnimation(attackName, false, false) self:playSkillFx(self.currActiveSkill) self.battleController:resetTimeSpeed() else self:moveBackToInitPosition() end else self:moveBackToInitPosition() end end function BattleUnitComp:enterNormalAttackState() self.attackOver = false self.attackTime = 0 self.skillSlowDownTime = nil 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.INIT_POS_X + skill:getMoveTypeParams() self.moveDirection = 1 else self.targetX = BattleConst.INIT_POS_X - skill:getMoveTypeParams() self.moveDirection = -1 end else self.isMove = false local attackName = skill:getSkillAttackName() self.currAttackDuration = self:getAnimationDuration(attackName) self.currAttackKeyTime = self:getKeyFrameTime(skill, 1, attackName) self:playSkillSound(skill:getSkillSound(), skill:getSoundDelay()) 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 -- 到达关键后使用 local skill = self.unitEntity:getNormalSkill() local isFinalBlock = skill and skill:isFinalBlock(self.currAttackBlockIndex) or false if self.normalSkillCount == 1 and isFinalBlock then -- 如果是最后一次攻击,那么敌人受到这次攻击可以开始嗝屁了 self.team:setCentralizedAttack(false) self.team:setIsFinalBlock(isFinalBlock) end self:onSkillTakeEffect(skill, isFinalBlock, self.validEffectIdx) if self.normalSkillCount == 1 and isFinalBlock then -- 如果是最后一次攻击,那么可以开始跑血条了 self.battleController:setIsPauseHpProgress(false) end if skill then local attackName = skill:getSkillAttackName() self.currAttackKeyTime = self:getKeyFrameTime(skill, self.currAttackBlockIndex + 1, attackName) else self.currAttackKeyTime = 0 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, conditionResult) if buffEffect.buff:needShowName() and buffEffect.buff:getShowName(true) then local direction = BattleConst.EFFECT_TYPE_MOVE_R local x, y = self.team:getMainUnitLocalPosition(self) x = x + 80 if self.side == BattleConst.SIDE_ATK then direction = BattleConst.EFFECT_TYPE_MOVE_L x = x - 80 end y = y - 120 local time = Time:getServerTime() if self.lastAddBuffTime == time then self.lastSameTimeAddBuffCount = self.lastSameTimeAddBuffCount + 1 else self.lastAddBuffTime = time self.lastSameTimeAddBuffCount = 0 end time = self.lastSameTimeAddBuffCount * BattleConst.EFFECT_NUMBER_INTERVAL local showCombo = conditionResult == BattleConst.SKILL_CONDITION_RESULT.CONDITION_PASS self:showEffectNumber(BattleConst.EFFECT_COLOR_SPECIAL, direction, buffEffect.buff:getShowName(true), x - 20, y - 20, time, showCombo) end return self.team:addBuff(buffEffect) end function BattleUnitComp:getBuffCountByName(buffName) return self.team:getBuffCountByName(buffName) end function BattleUnitComp:updateBuffState(buff, num) self.team:updateBuffState(buff, num) end function BattleUnitComp:removeAllBuff() self.team:removeAllBuff() end function BattleUnitComp:removeBuffByName(buffName) self.team:removeBuffByName(buffName) end function BattleUnitComp:putCacheBuff(buffEffect) self.team:putCacheBuff(buffEffect) end function BattleUnitComp:putCacheBuffByDecr(buffDecr) self.team:putCacheBuffByDecr(buffDecr) end function BattleUnitComp:popCacheBuffByDecr(buffDecr) self.team:popCacheBuffByDecr(buffDecr) end function BattleUnitComp:onSkillTakeEffect(skill, isFinalBlock, validEffectIdx) if isFinalBlock == nil then isFinalBlock = true end if isFinalBlock then skill:endUse() if skill:isAttackOverActive() then BATTLE_BOARD_SKILL_HANDLE.activeAttackOverSkill(self, skill, self.battleController) end 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 -- 格挡成功 target:playBlock() return end end end end local succ = false local effectStartIdx = 1 local effectEndIdx = 1 if validEffectIdx then effectStartIdx = validEffectIdx[1] + 1 effectEndIdx = validEffectIdx[2] else effectEndIdx = #effectList end for i = effectStartIdx, effectEndIdx do local effect = effectList[i] if effect then local conditionResult = self:judgeSkillEffectCondition(skill, i) if conditionResult ~= BattleConst.SKILL_CONDITION_RESULT.NOT_PASS then local isConditionPass = conditionResult == BattleConst.SKILL_CONDITION_RESULT.CONDITION_PASS if isConditionPass then self.team:addHurtComboTag(BattleConst.COMBO_DEFAULT_POSITION) end if self:takeEffect(effect, target, conditionResult) then if isConditionPass and skill:getComboPosition() then self.team:addHurtComboTag(skill:getComboPosition()) end succ = true end if isConditionPass then self.team:reduceHurtComboTag(BattleConst.COMBO_DEFAULT_POSITION) end end else break end end if succ then if skill:getIsHurtType() then self.team:addCombo() end if isFinalBlock 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 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 local blocks = skill:getEffectBlock() if blocks then local soundIndex = 1 for i, endIdx in ipairs(blocks) do if endIdx == effectEndIdx then soundIndex = i break end end if soundHit[soundIndex] and soundHit[soundIndex] > 0 then self:playSkillSound(soundHit[soundIndex], 0) end end end end end function BattleUnitComp:judgeSkillEffectCondition(skill, index) if not skill or not skill:haveBuffCondition() then return BattleConst.SKILL_CONDITION_RESULT.NO_CONDITION end local rel = skill:getBuffConditionRel(index) if not rel then return BattleConst.SKILL_CONDITION_RESULT.NO_CONDITION end local buffConditionIndex, conditionRel = rel[1], rel[2] local buffConditions = skill:getBuffCondition(buffConditionIndex) return BATTLE_SKILL_CONDITION_HANDLE.judgeSkillEffectCondition(buffConditions, conditionRel, self.battleController, self.side) end function BattleUnitComp:takeEffect(buff, target, conditionResult) local ratio = buff:getRatio() if ratio < DEFAULT_FACTOR then if BattleHelper:random(1, DEFAULT_FACTOR) > ratio then -- 没有通过命中概率 return false end end if self:getIsFrozen() and buff:isIncreaseGain() then -- 冻结状态,缓存增益buff if buff:getRound() > 0 then local buffEffect = BattleHelper:getBuffEffect() buffEffect.buff = buff buffEffect.result = nil buffEffect.round = buff:getRound() buffEffect.target = target buffEffect.sender = self target:putCacheBuff(buffEffect) end return end if target.unitEntity:getIsInvalidControl() then -- 控制效果 if buff:getBuffType() == BattleConst.BUFF_TYPE.CONTROL then return elseif buff:getName() == BattleConst.BUFF_NAME.FROZEN or buff:getName() == BattleConst.BUFF_NAME.IMPRISON then return 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 buffEffect = target:addBuff(buffEffect, conditionResult) end if buffEffect and buffEffect.result then return 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:reTakeEffectByBuffEffect(buffEffect) local round = buffEffect.round local target = buffEffect.target local buff = buffEffect.buff if round > 0 then buffEffect.result = nil buffEffect = target:addBuff(buffEffect) end if buffEffect and buffEffect.result then return 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, damageOrCureType) if self:getIsClear() then return 0 end if self.currState == UNIT_STATE.DEAD then return 0 end if num == 0 then return 0 end if num < 0 and atker.unitEntity:getBeDmgToHeal() > 0 then num = -num * atker.unitEntity:getBeDmgToHeal() // DEFAULT_FACTOR end atker.unitEntity:addDamageCount(num) local showHurtCombo = atker:getHurtComboTag() if effectType ~= EFFECT_TYPE.DIRECT then showHurtCombo = false end local shieldHpBefore = self.unitEntity:getShieldHp() local hpRealReduce = self.unitEntity:takeDamageOrCure(num) if hpRealReduce < 0 and self.side == BattleConst.SIDE_DEF then -- 实际掉血了 self:addBattleExp(atker, hpRealReduce) 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.team:getMainUnitLocalPosition(self) local damage = num local time = Time:getServerTime() if num < 0 then -- 伤害 local delayTime = 0 if shieldHpDiff < 0 then damage = damage - shieldHpDiff delayTime = BattleConst.EFFECT_NUMBER_DELAY if self.lastBlueNumberTime == time then self.lastSameTimeBlueCount = self.lastSameTimeBlueCount + 1 else self.lastBlueNumberTime = time self.lastSameTimeBlueCount = 0 end delayTime = delayTime + self.lastSameTimeBlueCount * BattleConst.EFFECT_NUMBER_INTERVAL if self.side == BattleConst.SIDE_ATK then self:showEffectNumber(BattleConst.EFFECT_COLOR_BLUE, BattleConst.EFFECT_TYPE_MOVE_L, shieldHpDiff, x, y, 0, showHurtCombo) else self:showEffectNumber(BattleConst.EFFECT_COLOR_BLUE, BattleConst.EFFECT_TYPE_MOVE_R, shieldHpDiff, x, y, 0, showHurtCombo) end end if damage < 0 then if self.lastBlueNumberTime == time then self.lastSameTimeBlueCount = self.lastSameTimeBlueCount + 1 else self.lastBlueNumberTime = time self.lastSameTimeBlueCount = 0 end delayTime = delayTime + self.lastSameTimeBlueCount * BattleConst.EFFECT_NUMBER_INTERVAL if effectStatus == HURT_STATE_CRIT then self:showEffectNumber(BattleConst.EFFECT_COLOR_RED, BattleConst.EFFECT_TYPE_CRIT, "c" .. damage, x, y, delayTime, showHurtCombo) else local effectColor = BattleConst.EFFECT_COLOR_RED local damageStr = damage local direction = BattleConst.EFFECT_TYPE_MOVE_R local elementType = BattleHelper:getDamageOrCureType2MatchType(damageOrCureType) local weekNessValue = self.unitEntity:getWeakness(elementType) if weekNessValue > 0 then damageStr = "a" .. damage else local decGmgValue = self.unitEntity:getDecDmg(elementType) if decGmgValue > 0 then effectColor = BattleConst.EFFECT_COLOR_WHILTE damageStr = "d" .. damage end end if self.side == BattleConst.SIDE_ATK then direction = BattleConst.EFFECT_TYPE_MOVE_L end self:showEffectNumber(effectColor, direction, damageStr, x, y, delayTime, showHurtCombo) end end if atker:getCurAttackActionState() == BattleConst.ATTACK_ACTION_STATE.NORMAL then if self.unitEntity:getCounterAttack() > 0 then -- 触发反击 local counterattack = self.unitEntity:getCounterAttack() if counterattack > DEFAULT_FACTOR or BattleHelper:random(1, DEFAULT_FACTOR) <= counterattack then -- 通过命中概率 self.unitEntity:addCounterAttackCount(1) self.battleController:showCounterAttack(self.unitEntity:getCounterAttackCount(), self.side) end end end if effectType == EFFECT_TYPE.DIRECT then if hp > 0 and self.unitEntity:getShieldRebound() then -- 伤害反弹 atker:takeDamageOrCure(self, num*self.unitEntity:getShieldRebound() // DEFAULT_FACTOR, EFFECT_TYPE.REBOUND, 0, BattleConst.BUFF_NAME.SHIELD_REBOUND_200) end if self.unitEntity:getBeSucked() > 0 then -- 吸血 atker:takeDamageOrCure(self, -num*self.unitEntity:getBeSucked() // DEFAULT_FACTOR, EFFECT_TYPE.HEAL, 0, BattleConst.SPECIAL_DAMAGE_OR_CURE_TYPE.BE_SUCKED) end if self:getIsLethargy() then -- 移除昏睡 self:removeBuffByName(BattleConst.BUFF_NAME.LETHARGY) end if self.unitEntity:getThorns() > 0 then -- 反伤 atker:takeDamageOrCure(self, num*self.unitEntity:getThorns() // DEFAULT_FACTOR, EFFECT_TYPE.REBOUND, 0, BattleConst.BUFF_NAME.THORNS) end end elseif num > 0 then -- 治疗 if self.lastHealTime == time then self.lastSameTimeHealCount = self.lastSameTimeHealCount + 1 else self.lastHealTime = time self.lastSameTimeHealCount = 0 end local delayTime = self.lastSameTimeHealCount * BattleConst.EFFECT_NUMBER_INTERVAL self:showEffectNumber(BattleConst.EFFECT_COLOR_GREEN, BattleConst.EFFECT_TYPE_BUFF, "+" .. num, x, y, delayTime) end self:refreshHpSkin() local hpPercent = self.unitEntity:getHpPercent() self.battleController:refreshHp(self.side, hp, hpPercent) if not atker:getIsFinalBlock() or atker:getIsCentralizedAttack() then if damage < 0 then self.team:playHurt() end else if self.unitEntity:getIsDead() then if damageOrCureType == BattleConst.BUFF_NAME.BURN then local team = self.battleController:getOtherSideTeam(self.side) team:checkPassiveEvent(BattleConst.PASSIVE_EVENT.ON_DEAD_BY_BURN) end if self.unitEntity:getAttrValue(BattleConst.ATTR_NAME.BLEED) > 0 then local team = self.battleController:getOtherSideTeam(self.side) team:checkPassiveEvent(BattleConst.PASSIVE_EVENT.ON_DEAD_WITH_BLEED) end if self.actionOverCallback then -- 被反击时死亡 self:onAttackOver() end self.battleController:resetTimeSpeed(true) self.team:changeDead() -- 因为buff生效对象有可能不是当前主英雄,会导致状态不对,所以使用主英雄来播放动画 elseif damage < 0 then self.team:playHurt() end end if hp > 0 then self.team:checkPassiveEvent(PASSIVE_EVENT.HP_LOWER_THAN, atker, hpPercent) else self.team:checkPassiveEvent(PASSIVE_EVENT.ON_DEAD, atker) end end function BattleUnitComp:addBattleExp(atker, hpRealReduce) if not atker or hpRealReduce > 0 then return end local hpExp = self.unitEntity:getHpExp() if hpExp then -- 有血量分段经验 local beforeHp = math.min(self.unitEntity:getHp() - hpRealReduce, self.unitEntity:getMaxHp()) local beforeHpp = self.unitEntity:getHpPercent(beforeHp) * DEFAULT_FACTOR local nowHpp = self.unitEntity:getHpPercent() * DEFAULT_FACTOR local curExpIndex local curHppSpan local beforeExpIndex local beforeHppSpan for index, info in ipairs(hpExp) do local nextHpp = 0 if hpExp[index + 1] then nextHpp = hpExp[index + 1].hpp end if nowHpp < info.hpp and nowHpp >= nextHpp then -- 当前所处经验段 if not curExpIndex then curExpIndex = index curHppSpan = info.hpp - nextHpp end end if beforeHpp < info.hpp and beforeHpp >= nextHpp then -- 上一次所处经验段 if not beforeExpIndex then beforeExpIndex = index beforeHppSpan = info.hpp - nextHpp end end end if not curExpIndex then return end if not beforeExpIndex then beforeExpIndex = curExpIndex beforeHppSpan = curHppSpan end local addExp = 0 if beforeExpIndex == curExpIndex then -- 前后都在一个经验段 local exp = hpExp[curExpIndex].exp local expTime = atker.unitEntity:getExpTime() addExp = math.floor(exp * (beforeHpp - nowHpp) / curHppSpan) if expTime > 0 then addExp = addExp * (DEFAULT_FACTOR + expTime) // DEFAULT_FACTOR end elseif beforeExpIndex < curExpIndex then for i = beforeExpIndex + 1, curExpIndex do local exp = hpExp[i - 1].exp local hpp = hpExp[i].hpp local hppSpan = hpExp[i - 1].hpp - hpp addExp = addExp + exp * (beforeHpp - hpp) / hppSpan beforeHpp = hpp end local exp = hpExp[curExpIndex].exp addExp = math.floor(addExp + exp * (beforeHpp - nowHpp) / curHppSpan) local expTime = atker.unitEntity:getExpTime() if expTime > 0 then addExp = addExp * (DEFAULT_FACTOR + expTime) // DEFAULT_FACTOR end end self.battleController:addBattleExp(addExp) else local exp = self.unitEntity:getExp() if exp and 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 end function BattleUnitComp:showEffectNumber(colorType, effectType, num, x, y, delayTime, showCombo) local body = self.unitEntity:getBody() if self.team:getMainUnit() then body = self.team:getMainUnit().unitEntity:getBody() end local addY = BattleConst.MIN_NODE_HEIGHT[body] or BattleConst.MIN_NODE_HEIGHT_DEFAULT self.battleController:showEffectNumber(colorType, effectType, num, x, y + addY, delayTime, showCombo) end function BattleUnitComp:refreshHpSkin() if self.unitEntity:getHpSkinInfo() then local skin local hpp = self.unitEntity:getHpPercent() * DEFAULT_FACTOR for index, info in ipairs(self.unitEntity:getHpSkinInfo()) do if hpp <= info.hpp then skin = info.skin break end end if self.lastHpSkin == skin then return end self.lastHpSkin = skin self.baseObject:setSkin(skin) end 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:playSkillSound(soundName, delay) if not soundName then return end delay = delay or 0 BattleHelper:playSkillSound(soundName, delay) 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.flip then effectObj:setLocalEulerAngles(0, 180, 0) effectObj:setLocalScale(fxScale, fxScale, fxScale) else effectObj:setLocalEulerAngles(0, 0, 0) 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:removeFx(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) elseif self.currState == UNIT_STATE.FROZEN then self:updateFrozen(dt) elseif self.currState == UNIT_STATE.VERITGO then self:updateVeritgo(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