local PetSystem = {} local private = {} local Idle_State = LuaConst.StateType.Idle local FP = FP local FP_ZERO = FP.Const.ZERO local FP_V2_IsDistanceSqrGreater = FP.V2.IsDistanceSqrGreater local FP_V2_DistanceSqr = FP.V2.DistanceSqr local FP_V2_SqrMagnitude = FP.V2.SqrMagnitude local Const_PetAttackNotAuto_Frame = DataSheetManager.ConstData:GetDataByID("PetAttackSetCD").IntValue * LuaConst.LogicFrameRate --当主人打中敌人后,宠物在多久帧内可攻击(10秒) local Const_RideSpeedSqr = FP.newV1(0.436)--一般骑马速度都是 12 m/s .为了节约性能这个就写死了 。 此值为1帧骑马时的移动距离 local Const_RunSpeedSqr = FP.newV1(0.09)--一般走路速度都是 6 m/s .为了节约性能这个就写死了 。 此值为1帧走路时的移动距离 local Const_FollowPosOffset = FP.newV2(2,-0.5) -- 宠物要追随的相对主人的偏移位置 local Const_DeadFollowPosOffset = FP.newV2(1,0.5) -- 宠物要追随的相对主人的偏移位置(死亡时的) local Const_RidingFollowPosOffset = FP.newV2(1,-1.5) -- 宠物要追随的相对主人的偏移位置(骑马跑时) local Const_IdleShowFrame = 200 -- 在idle时每10秒(200帧)播放一次idle_show local Const_IdleShowDuringFrame = 133 -- idle_show时长(美术约定为6.66秒即 134帧) local Const_FollowDistanceSqr = 12 -- 与主人距离大于此值则向 目标点追随(注意一定要大于 [Const_FollowPosOffset 到主人的距离 + Const_StopFollowDistanceSqr]) local Const_FollowDistanceSqr_Town = 5 -- (主城中)与主人距离大于此值则向 目标点追随(注意一定要大于 [Const_FollowPosOffset 到主人的距离 + Const_StopFollowDistanceSqr]) local Const_AllowAttackDistanceSqr = 64 -- 在此范围内,优先释放技能 local Const_DeadIdleDistanceSqr = 1.5 -- 当主人死亡后与主人距离大于此值则向 主人跑去( 要大于 Const_DeadFollowPosOffset的模) local Const_StopFollowDistanceSqr = 0.01 -- 与追随点距离小于此值则idle local Const_ConveyDisSqr = 400 -- 超过20米则瞬移回主人旁边 local IDLERUN_SEARCH_TARGET_CD_FRAME = 20 --在Idle或Run状态下每隔多久锁一次敌 local PetStateEnum = { None = nil, Idle = 1, Run = 2, Attack = 3, IdleShow = 4, } local PetAnimatorEnum = { Idle = "idle_loop", Run = "run_loop", NormalAttack = "n1", SkillAttack = "s1", IdleShow = "show", } local PetSkillEnum = LuaConst.PetSkillEnum local temp_V1 = FP.newV1(0) local temp_V2 = FP.newV2(0,0) local temp_Step = FP.newV1(0) local temp_StepSqr = FP.newV1(0) local temp_SqrV1 = FP.newV1(0) function PetSystem.OnLogicUpdate(entity) local petComp = entity.components.pet if not petComp then return end -- 1.更新时间 ms if petComp.skillCurCD > 0 then petComp:SetSkillCurCD(petComp.skillCurCD - LuaConst.TimeSpanMs) end if petComp.normalSkillCurCD > 0 then petComp:SetNormalSkillCurCD(petComp.normalSkillCurCD - LuaConst.TimeSpanMs) end --2.状态Update private.UpdateState(petComp) end --region pet的微型状态机 local forwardTemp_V2 = FP.newV2(0,0) function private.UpdateState(petComp) local curState = petComp.curPetState if curState == PetStateEnum.None then private.IdleStateIn(petComp) elseif curState == PetStateEnum.Idle then private.IdleStateUpdate(petComp) elseif curState == PetStateEnum.Run then private.RunStateUpdate(petComp) elseif curState == PetStateEnum.Attack then private.AttackStateUpdate(petComp) elseif curState == PetStateEnum.IdleShow then private.IdleShowStateUpdate(petComp) end end --返回宠物的的跟随位置 function private.SetPetFollowPos(petComp) local moveComp = petComp.entity.components.move local forward = moveComp:GetLogicRotateDir() local offsetPos if petComp.entity:CheckHasBuffState(LuaConst.SpecialBuffs.InRiding) then offsetPos = Const_RidingFollowPosOffset else offsetPos = Const_FollowPosOffset --petComp.entity:IsAlive() and Const_FollowPosOffset or Const_DeadFollowPosOffset end FP.V2.GetOffsetToForward(forward, offsetPos, temp_V2) temp_V2:Add(moveComp.LogicPos) if petComp.isInTownLevel and petComp.entity.components.aiBase.CurStateType == Idle_State then -- 在主城如果玩家停下来了,宠物要到主人身边,防止被地形阻挡而抖动(可穿墙) petComp:SetPetFollowPos(temp_V2) else petComp:SetPetFollowPos(LuaShoveManager.GetFinalInitPos(temp_V2)) end end -- idle function private.IdleStateIn(petComp) petComp.searchTargetCdFrame = 0 petComp:SetPetState(PetStateEnum.Idle) petComp.entity.ExpEntity:PlayPetAnimation(PetAnimatorEnum.Idle) petComp.idleShowPlayFrame = LogicUpdateManager._clientHandleFrame + Const_IdleShowFrame end function private.IdleStateUpdate(petComp) --0. 主人死亡了,则跑到主人的旁边 if not petComp.entity:IsAlive() then --[[由于现在改为了 主人死亡宠物则消失,故注释掉;但为了防止策划又变卦,这代码先留着。 if FP_V2_IsDistanceSqrGreater(petComp.entity:GetLogicPos(), petComp.logicPos, Const_DeadIdleDistanceSqr) then private.IdleStateOut(petComp) private.RunStateIn(petComp) end --]] return end --1. 主城尝试进入idle_show动画 if petComp.isInTownLevel then -- 主城中要紧紧跟随主人 --1.检查是否与主人太远了,向目标点移动 if FP_V2_IsDistanceSqrGreater(petComp.entity:GetLogicPos(), petComp.logicPos, Const_FollowDistanceSqr_Town) then private.IdleStateOut(petComp) private.RunStateIn(petComp) return end if petComp.idleShowPlayFrame == LogicUpdateManager._clientHandleFrame then private.IdleStateOut(petComp) private.IdleShowStateIn(petComp) end else -- 2.与主人距离在范围内,尝试释放技能 petComp.searchTargetCdFrame = petComp.searchTargetCdFrame - 1 local canUse, skillType, target, faceToTarget = private.TryGetCanUseSkill(petComp) if canUse then private.IdleStateOut(petComp) private.AttackStateIn(petComp, skillType, target, faceToTarget) return end if FP_V2_IsDistanceSqrGreater(petComp.entity:GetLogicPos(), petComp.logicPos, Const_FollowDistanceSqr) then private.IdleStateOut(petComp) private.RunStateIn(petComp) return end end end function private.IdleStateOut(petComp) petComp.idleShowPlayFrame = -1 end -- idleShow -- 只有主城才会有这个状态机 function private.IdleShowStateIn(petComp) petComp:SetPetState(PetStateEnum.IdleShow) petComp.entity.ExpEntity:PlayPetAnimation(PetAnimatorEnum.IdleShow) petComp.idleShowToIdlePlayFrame = LogicUpdateManager._clientHandleFrame + Const_IdleShowDuringFrame end function private.IdleShowStateUpdate(petComp) --1.检查是否与主人太远了,向目标点移动 if FP_V2_IsDistanceSqrGreater(petComp.entity:GetLogicPos(), petComp.logicPos, Const_FollowDistanceSqr_Town) then private.IdleShowStateOut(petComp) private.RunStateIn(petComp) return end --2. 进入idle动画 if petComp.idleShowToIdlePlayFrame == LogicUpdateManager._clientHandleFrame then private.IdleShowStateOut(petComp) private.IdleStateIn(petComp) end end function private.IdleShowStateOut(petComp) petComp.idleShowToIdlePlayFrame = -1 end --Run function private.RunStateIn(petComp) petComp.searchTargetCdFrame = 0 petComp:SetPetState(PetStateEnum.Run) petComp.entity.ExpEntity:PlayPetAnimation(PetAnimatorEnum.Run) private.SetPetFollowPos(petComp) end function private.RunStateUpdate(petComp) --0. 检查是否到了需要更新追随点时机了 if petComp.findFollowPosBatchIndex < 0 or LogicUpdateManager._clientHandleFrame % 10 == petComp.findFollowPosBatchIndex then private.SetPetFollowPos(petComp) end petComp.searchTargetCdFrame = petComp.searchTargetCdFrame - 1 --1. 检查主人死亡后,跑去守灵范围 if not petComp.entity:IsAlive() then --[[由于现在改为了 主人死亡宠物则消失,故注释掉;但为了防止策划又变卦,这代码先留着。 if FP_V2_IsDistanceSqrGreater(petComp.followPos, petComp.logicPos, Const_StopFollowDistanceSqr) then private.PetMoveToFollowPos(petComp) else temp_V2:Reset(petComp.entity:GetLogicPos()):Sub(petComp.logicPos):Normalize() petComp:SetPetLogicDir(temp_V2) petComp.entity.ExpEntity:ApplyPetExpTrans(false) private.RunStateOut(petComp) private.IdleStateIn(petComp) end--]] -- 直接设置宠物位置 petComp:SetPetLogicPos(petComp.followPos) petComp.entity.ExpEntity:ApplyPetExpTrans(false) return end FP_V2_DistanceSqr(petComp.logicPos, petComp.followPos, temp_V1) -- temp_V1 =disSqr -- 2. 距离太远向followPos点瞬移 if FP.Greater(temp_V1, Const_ConveyDisSqr) then petComp:SetPetLogicPos(petComp.followPos) return end -- 3.与主人距离在范围内,尝试释放技能 local canUse, skillType, target, faceToTarget = private.TryGetCanUseSkill(petComp) if canUse then private.RunStateOut(petComp) private.AttackStateIn(petComp, skillType, target, faceToTarget) return end --4. 继续向followPos点跑,直到基本到了FollowPos点上 if FP.Greater(temp_V1, Const_StopFollowDistanceSqr) then private.PetMoveToFollowPos(petComp) return end -- 6. 跑到了位置进入idle private.RunStateOut(petComp) private.IdleStateIn(petComp) end function private.RunStateOut(petComp) private.SetPetFollowPos(petComp) end --attack function private.AttackStateIn(petComp, skillType, target, faceToTarget) petComp:SetPetState(PetStateEnum.Attack) petComp:SetPetSkillType(skillType, target) local anim local psEffect -- 进入CD if skillType == PetSkillEnum.Skill then petComp:SetSkillCurCD(petComp.skillMaxCD) anim = PetAnimatorEnum.SkillAttack psEffect = petComp.skillData.SkillEffect elseif skillType == PetSkillEnum.Normal then petComp:SetNormalSkillCurCD(petComp.normalSkillMaxCD) anim = PetAnimatorEnum.NormalAttack psEffect = petComp.petBattleData.NormalEffect end forwardTemp_V2:Reset(faceToTarget:GetLogicPos()):Sub(petComp.logicPos):Normalize() petComp:SetPetLogicDir(forwardTemp_V2) petComp.entity.ExpEntity:PlayPetAnimation(anim) petComp.entity.ExpEntity:ReceivePetPsEffect(psEffect) petComp.entity.ExpEntity:ApplyPetExpTrans(true) if skillType == PetSkillEnum.Skill then BattleEventSystem.OnUsePetSkill(petComp.entity) elseif skillType == PetSkillEnum.Normal then BattleEventSystem.OnUsePetNormalAttack(petComp.entity) end end function private.AttackStateUpdate(petComp) if petComp.missileBornFrame == LogicUpdateManager._clientHandleFrame then -- 发射子弹 private.PetAttackEntity(petComp, petComp.curSkillType) end if petComp.attackQuitFrame == LogicUpdateManager._clientHandleFrame then private.AttackStateOut(petComp) end --0. 检查是否到了需要更新追随点时机了 if LogicUpdateManager._clientHandleFrame % 10 == petComp.findFollowPosBatchIndex then private.SetPetFollowPos(petComp) end FP_V2_DistanceSqr(petComp.logicPos, petComp.followPos, temp_V1) -- temp_V1 =disSqr --. 继续向followPos点跑,直到基本到了FollowPos点上 if FP.Greater(temp_V1, Const_StopFollowDistanceSqr) then private.PetMoveToFollowPos(petComp, true) return end end function private.AttackStateOut(petComp) petComp:SetPetSkillType(nil) --检查是否与主人太远了,向目标点移动 if FP_V2_IsDistanceSqrGreater(petComp.entity:GetLogicPos(), petComp.logicPos, Const_FollowDistanceSqr) then private.RunStateIn(petComp) else private.IdleStateIn(petComp) end end -- 设置移动 function private.PetMoveToFollowPos(petComp, isHoldDir) temp_V2:Reset(petComp.followPos):Sub(petComp.logicPos) FP_V2_SqrMagnitude(temp_V2 ,temp_SqrV1) -- temp_SqrV1 到目标点的距离的平方 temp_V2:Normalize() -- temp_V2 = 朝向 if not isHoldDir then petComp:SetPetLogicDir(temp_V2) end local realSpeed if petComp.entity:CheckHasBuffState(LuaConst.SpecialBuffs.InRiding) then if temp_SqrV1 <= Const_RideSpeedSqr then temp_V2:Reset(petComp.followPos) else realSpeed = petComp.townFollowHorseSpd or petComp.entity.components.attr:GetAttrValue(LuaConst.AttrType.ridespd) temp_V2:Mul(realSpeed):Mul(LuaConst.TimeSpan):Add(petComp.logicPos) end else local spdEntity = petComp.entity.components.drive.DriveEntity or petComp.entity realSpeed = spdEntity.components.attr:GetAttrValue(LuaConst.AttrType.spd) temp_Step:Reset(realSpeed):Mul(LuaConst.TimeSpan) -- 一帧移动的距离 temp_StepSqr:Reset(temp_Step):Mul(temp_Step) if temp_SqrV1 <= temp_StepSqr then -- 当一帧就能到达时则设置为立刻到达 temp_V2:Reset(petComp.followPos) else temp_V2:Mul(temp_Step):Add(petComp.logicPos) end end petComp:SetPetLogicPos(temp_V2) petComp.entity.ExpEntity:ApplyPetExpTrans(false) end -- 检查释放技能 function private.TryGetCanUseSkill(petComp) -- 主城不能释放技能 if petComp.isInTownLevel then return false end -- 暂停了攻击 if petComp.pauseAttack then return false end --非自由攻击需判断主人打到敌人n秒内 if not petComp.autoAttack then if LogicUpdateManager._clientHandleFrame - petComp.lastHitOthersFrame > Const_PetAttackNotAuto_Frame then return false end end -- 普攻和技能都在CD中则跳出。能优化一下searchTarget的频率 if (petComp.skillData == nil or petComp.skillCurCD > 0) and petComp.normalSkillCurCD > 0 then return false else if (petComp.curPetState == PetStateEnum.Idle or petComp.curPetState == PetStateEnum.Run) and petComp.searchTargetCdFrame > 0 then return false end end if FP_V2_IsDistanceSqrGreater(petComp.entity:GetLogicPos(), petComp.logicPos, Const_AllowAttackDistanceSqr) then return false end -- 没有攻击目标时,不释放有子弹的攻击 local target = SystemManager.SearchPetSkillTarget(petComp.entity) petComp.searchTargetCdFrame = IDLERUN_SEARCH_TARGET_CD_FRAME -- 优先放技能,再放普攻 if petComp.skillData and petComp.skillCurCD <= 0 then -- petComp.skillData 判断是否有技能 if #petComp.skillData.BuffList > 0 and petComp.skillData.MissileID == 0 then -- 当技能效果仅仅只是给主人添加buff时,朝向主人。 return true, PetSkillEnum.Skill, target, petComp.entity end return target ~= nil, PetSkillEnum.Skill, target, target end if petComp.normalSkillCurCD <= 0 then return target ~= nil, PetSkillEnum.Normal, target, target end return false end --endregion pet的微型状态机 local positionTemp_V2 = FP.newV2(0,0) local offsetTemp_V2 = FP.newV2(0,0) --释放子弹 function private.PetAttackEntity(petComp, skillType) local petBattleData = petComp.petBattleData local targetPos = petComp.attackTargetPos -- 如果以后策划有需求子弹打向目标的实时位置则用 petComp.attackTarget:GetLogicPos() local offsetY = 0 local missileID positionTemp_V2:Reset() if skillType == PetSkillEnum.Normal then missileID = petBattleData.NormalMissileID if #petBattleData.NormalMissileOffset > 0 then offsetTemp_V2:Reset(petBattleData.NormalMissileOffset[1], petBattleData.NormalMissileOffset[3]) offsetY = petBattleData.NormalMissileOffset[2] FP.V2.GetOffsetToForward(forwardTemp_V2, offsetTemp_V2, positionTemp_V2) end if petBattleData.NormalMissileBornAtTarget then positionTemp_V2:Add(targetPos) else positionTemp_V2:Add(petComp.logicPos) end forwardTemp_V2:Reset(targetPos):Sub(petComp.logicPos):Normalize() MissileManager.DoCallPetMissile(petComp.entity, missileID, forwardTemp_V2, positionTemp_V2, petComp.attackTarget, offsetY) elseif skillType == PetSkillEnum.Skill then local petSkillData = petComp.skillData missileID = petSkillData.MissileID if missileID > 0 then if #petSkillData.MissileOffset > 0 then offsetTemp_V2:Reset(petSkillData.MissileOffset[1], petSkillData.MissileOffset[3]) offsetY = petSkillData.MissileOffset[2] FP.V2.GetOffsetToForward(forwardTemp_V2, offsetTemp_V2, positionTemp_V2) end if petSkillData.MissileBornAtTarget then positionTemp_V2:Add(targetPos) else positionTemp_V2:Add(petComp.logicPos) end forwardTemp_V2:Reset(targetPos):Sub(petComp.logicPos):Normalize() MissileManager.DoCallPetMissile(petComp.entity, missileID, forwardTemp_V2, positionTemp_V2, petComp.attackTarget, offsetY) end local buffList = petSkillData.BuffList --技能有时候是直接给主人添加buff,无子弹 if #buffList > 0 then for _, buffId in ipairs(buffList) do SystemManager.AddBuff(petComp.entity, buffId, petComp.entity) end end end end --region 换人、传送 等操作的处理 -- 换将后更新宠物的位置 function PetSystem.OnSwitchHero(toEntity) local petComp = toEntity.components.pet if petComp then petComp:ResetCacheData() petComp:SetPetInitPos() end end function PetSystem.OnRevive(entity) local petComp = entity.components.pet if petComp then petComp:ResetCacheData() petComp:SetPetInitPos() end end function PetSystem.OnConvey(entity) local petComp = entity.components.pet if petComp then petComp:SetPetInitPos() end end --[[ 回到出生点 --]] function PetSystem.BackToBornPoint(entity) local petComp = entity.components.pet if petComp then petComp:SetPetInitPos() end end function PetSystem.ReducePetSkillCDByPercent(entity, percent) local petComp = entity.components.pet if petComp then temp_V1:Reset(petComp.skillMaxCD):Mul(percent) local time = math.floor(FP.tonumberV1(temp_V1)) -- 不用定点数的地方那就要一定保证为int值 if petComp.skillCurCD > 0 then petComp:SetSkillCurCD(petComp.skillCurCD - time) end end end function PetSystem.ReducePetNormalOrSkillCD(entity, type, subCD) local petComp = entity.components.pet if petComp then if (type ==PetSkillEnum.Skill or type == 0) and petComp.skillCurCD > 0 then petComp:SetSkillCurCD(petComp.skillCurCD - subCD) end if (type ==PetSkillEnum.Normal or type == 0) and petComp.normalSkillCurCD > 0 then petComp:SetNormalSkillCurCD(petComp.normalSkillCurCD - subCD) end end end --endregion 换人、复活、传送 等操作的处理 return PetSystem