package fight import ( "blazing/common/utils" "blazing/logic/service/fight/action" "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" "blazing/modules/player/model" "reflect" "github.com/alpacahq/alpacadecimal" "github.com/barkimedes/go-deepcopy" "github.com/gogf/gf/v2/util/grand" ) // processSkillAttack 处理技能攻击逻辑 func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info.SkillEntity) { if attacker == nil || defender == nil || skill == nil { return } skill.AttackTimeC(attacker.Prop[5]) //计算命中 defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //计算闪避,然后修改对方命中),同时相当于计算属性无效这种 f.setEffectSkillContext(effect, skill, defender) effect.SkillHit_ex() return true }) attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //计算变威力 f.setEffectSkillContext(effect, skill, defender) effect.SkillHit() //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率 return true }) var originalProps [2][6]int8 var originalPetInfo [2]model.PetInfo attackerPet := attacker.CurrentPet() defenderPet := defender.CurrentPet() if attackerPet == nil || defenderPet == nil { return } //复制属性 originalProps[0], originalProps[1] = attacker.Prop, defender.Prop originalPetInfo[0], originalPetInfo[1] = attackerPet.Info, defenderPet.Info attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //计算变威力 f.setEffectSkillContext(effect, skill, defender) effect.CalculatePre() //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率 return true }) //技能命中+效果失效 这里就是修改效果命中为false //技能miss+效果生效 这里属于强制改命中效果,但是正常来说,技能miss掉后效果也应该失效 //技能失效+效果失效 attacker.AttackTime = skill.AttackTime attacker.SkillID = uint32(skill.XML.ID) //获取技能ID var SumDamage alpacadecimal.Decimal if skill.AttackTime != 0 { //如果命中 SumDamage = attacker.CalculatePower(defender, skill) attacker.CalculateCrit(defender, skill) //暴击计算 attacker.IsCritical = skill.Crit } //还原属性 attacker.Prop, defender.Prop = originalProps[0], originalProps[1] attackerPet.Info, defenderPet.Info = originalPetInfo[0], originalPetInfo[1] if attacker.IsCritical == 1 { //命中了才有暴击 //暴击破防 if skill.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 { defender.Prop[1] = 0 } else if skill.Category() == info.Category.SPECIAL && defender.Prop[3] > 0 { defender.Prop[3] = 0 } //暴击翻倍 SumDamage = SumDamage.Mul(alpacadecimal.NewFromInt(2)) } //到这里已经是强制miss或者命中,所以根本不存在强制miss改命中的情况,因为miss的时候不会执行到这里 if !skill.Hit { for _, effect := range attacker.EffectCache { effect.Alive(false) //我方效果命中 } //这时候将被覆盖的效果全部装回来enterturn for _, effect := range attacker.EffectLost { if effect.Duration() > 0 || effect.Duration() == -1 { //effect.Alive(true) attacker.AddEffect(effect.GetInput(), effect) } } } // 扣减防御方血量 attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { f.setEffectSkillContext(effect, skill, defender) effect.OnSkill() //调用伤害计算 return true }) defender.Damage(attacker, &info.DamageZone{ Damage: SumDamage, Type: info.DamageType.Red, }) } // IsNil 检查值是否为nil func IsNil(x interface{}) bool { if x == nil { return true } rv := reflect.ValueOf(x) return rv.Kind() == reflect.Ptr && rv.IsNil() } // copySkill 复制技能实体 func (f *FightC) copySkill(action *action.SelectSkillAction) *info.SkillEntity { if action == nil { return nil } if action.SkillEntity == nil { return nil } originalSkill, _ := deepcopy.Anything(action.SkillEntity) //备份技能 originalSkill.(*info.SkillEntity).Accuracy = action.SkillEntity.Accuracy //拷贝后命中丢失 return originalSkill.(*info.SkillEntity) } func (f *FightC) getSkillParticipants(skillAction *action.SelectSkillAction) (*input.Input, *input.Input) { if skillAction == nil { return nil, nil } return f.GetInputByAction(skillAction, false), f.GetInputByAction(skillAction, true) } // setEffectSkillContext 统一设置技能阶段 effect 上下文。 // 规则: // 1) Our/Opp 由 ExecWithOpponent 负责写入(carrier / 对位)。 // 2) Target 统一表示“本次动作被选中的目标位”,在攻防双方 hook 中保持一致。 func (f *FightC) setEffectSkillContext(effect input.Effect, skill *info.SkillEntity, target *input.Input) { if effect == nil { return } ctx := effect.Ctx() ctx.SkillEntity = skill if target != nil { ctx.Target = target } } func (f *FightC) setEffectTarget(effect input.Effect, target *input.Input) { if effect == nil || target == nil { return } effect.Ctx().Target = target } // plannedOpponentForCarrier 为旧 effect 提供“当前动作对应目标”上下文: // 1) 若 carrier 是本回合出手方,返回其动作目标。 // 2) 若无匹配动作,返回 nil,交给 Input 默认 OppTeam 回退。 func (f *FightC) plannedOpponentForCarrier(carrier *input.Input, acts ...*action.SelectSkillAction) *input.Input { if carrier == nil { return nil } for _, act := range acts { if act == nil { continue } attacker, defender := f.getSkillParticipants(act) if attacker == carrier { return defender } } return nil } func (f *FightC) collectAttackValues(inputs []*input.Input) []model.AttackValue { values := make([]model.AttackValue, 0, len(inputs)) for actorIndex, fighter := range inputs { if fighter == nil || fighter.AttackValue == nil { continue } attackValue := *fighter.AttackValue attackValue.ActorIndex = uint32(actorIndex) values = append(values, attackValue) } return values } func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo { result := info.NoteUseSkillOutboundInfo{} result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValues(f.Our)...) result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValues(f.Opp)...) return result } // enterturn 处理战斗回合逻辑 // 回合有先手方和后手方,同时有攻击方和被攻击方 func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) { //双方首发精灵登场时,依挑战方机制:房主方先判定,挑战方后判定 //双方非首发精灵登场时,根据切换先后判定(看手速) // 神罗、圣华登场时魂免,“登场时xx”等效果 //阿枫的效果也在这里判断 if f.closefight { return } f.Broadcast(func(ff *input.Input) { ff.EffectCache = make([]input.Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来 ff.EffectLost = make([]input.Effect, 0) opponent := f.plannedOpponentForCarrier(ff, firstAttack, secondAttack) ff.ExecWithOpponent(opponent, func(effect input.Effect) bool { //回合开始前 f.setEffectTarget(effect, opponent) effect.TurnStart(firstAttack, secondAttack) return true }) }) for _, skillAction := range []*action.SelectSkillAction{firstAttack, secondAttack} { attackerInput, _ := f.getSkillParticipants(skillAction) if attackerInput == nil { continue } f.setActionAttackValue(skillAction) attackerInput.Parseskill(skillAction) } f.Broadcast(func(fighter *input.Input) { opponent := f.plannedOpponentForCarrier(fighter, firstAttack, secondAttack) fighter.ExecWithOpponent(opponent, func(effect input.Effect) bool { //回合开始前 f.setEffectTarget(effect, opponent) effect.ComparePre(firstAttack, secondAttack) //先结算技能的优先级 return true }) fighter.ResetAttackValue() }) f.First, f.Second = f.primaryOur(), f.primaryOpp() switch { case firstAttack != nil && secondAttack != nil: f.First, _ = f.getSkillParticipants(firstAttack) f.Second, _ = f.getSkillParticipants(secondAttack) case firstAttack != nil: f.First, f.Second = f.getSkillParticipants(firstAttack) case secondAttack != nil: f.First, f.Second = f.getSkillParticipants(secondAttack) } if f.First == nil { f.First = f.primaryOur() } if f.Second == nil { f.Second = f.primaryOpp() } if firstAttack != nil && secondAttack != nil { switch { case firstAttack.SkillEntity.XML.Priority < secondAttack.SkillEntity.XML.Priority: firstAttack, secondAttack = secondAttack, firstAttack //互换先手权 f.First, f.Second = f.Second, f.First case firstAttack.SkillEntity.XML.Priority == secondAttack.SkillEntity.XML.Priority: if f.Second.GetProp(4).Cmp(f.First.GetProp(4)) > 0 { firstAttack, secondAttack = secondAttack, firstAttack //互换先手权 f.First, f.Second = f.Second, f.First } if f.Second.GetProp(4).Cmp(f.First.GetProp(4)) == 0 { if grand.Meet(1, 2) { //随机出手 firstAttack, secondAttack = secondAttack, firstAttack //互换先手权 f.First, f.Second = f.Second, f.First } } } } if firstAttack == nil && secondAttack == nil { firstAttack, secondAttack = secondAttack, firstAttack //互换先手权 f.First, f.Second = f.Second, f.First } var attacker, defender *input.Input f.TrueFirst = f.First //开始回合操作 for i := 0; i < 2; i++ { var originalSkill *info.SkillEntity //原始技能 var currentSkill *info.SkillEntity //当前技能 var currentAction *action.SelectSkillAction if i == 0 { currentAction = firstAttack attacker, defender = f.getSkillParticipants(firstAttack) originalSkill = f.copySkill(firstAttack) //先手阶段,先修复后手效果 f.Second.RecoverEffect() } else { currentAction = secondAttack attacker, defender = f.getSkillParticipants(secondAttack) originalSkill = f.copySkill(secondAttack) //取消后手历史效果 f.Second.ReactvieEffect() } if attacker == nil { attacker = f.First } if defender == nil { defender = f.Second } currentSkill = originalSkill defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //这个是能否使用技能 f.setEffectSkillContext(effect, currentSkill, defender) return effect.ActionStartEx(firstAttack, secondAttack) }) canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能 f.setEffectSkillContext(effect, currentSkill, defender) return effect.ActionStart(firstAttack, secondAttack) }) attackerPet := attacker.CurrentPet() defenderPet := defender.CurrentPet() canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attackerPet != nil && attackerPet.Info.Hp > 0 if !canUse { attacker.RecoverEffect() currentSkill = nil if i == 0 { //先手方被控,这时候应该算做未出手状态 f.TrueFirst = defender for _, effect := range defender.EffectCache { effect.IsFirst(true) } } //先手权不一定出手 } else { f.setActionAttackValue(currentAction) for _, effect := range attacker.EffectCache { effect.IsFirst(true) } f.processSkillAttack(attacker, defender, currentSkill) currentSkill = originalSkill //还原技能 _, skill, ok := utils.FindWithIndex(attackerPet.Info.SkillList, func(item model.SkillInfo) bool { return item.ID == currentSkill.Info.ID }) if ok { usecount := 1 attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果 f.setEffectSkillContext(effect, currentSkill, defender) effect.HookPP(&usecount) return true }) skill.Use(usecount) } } if defenderPet != nil && defenderPet.Info.Hp > 0 { //技能使用后 defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果 f.setEffectSkillContext(effect, currentSkill, defender) effect.Skill_Use_ex() return true }) } if attackerPet != nil && attackerPet.Info.Hp > 0 { //技能使用后 attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果 f.setEffectSkillContext(effect, currentSkill, defender) effect.Skill_Use() return true }) } //技能使用后 defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果 f.setEffectSkillContext(effect, currentSkill, defender) effect.Action_end_ex() return true }) //技能使用后 attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果 f.setEffectSkillContext(effect, currentSkill, defender) effect.Action_end() return true }) if defenderPet != nil && attackerPet != nil && defenderPet.Info.Hp <= 0 && attackerPet.Info.Hp <= 0 { //先手方死亡,触发反同归于尽 attackerPet.Info.Hp = 1 } if defenderPet != nil && defenderPet.Info.Hp <= 0 { f.TURNOVER(defender) break } if attackerPet != nil && attackerPet.Info.Hp <= 0 { f.TURNOVER(attacker) break } } f.Broadcast(func(ff *input.Input) { ff.GenSataus() ff.Exec(func(t input.Effect) bool { //这个是能否使用技能 //结算状态 t.TurnEnd() //返回本身结算,如果false,说明不能使用技能了 return true }) ff.GenInfo() ff.SnapshotTurnProp() }) if f.TrueFirst != f.First { f.First, f.Second = f.Second, f.First } if f.LegacyGroupProtocol { f.sendLegacyRoundBroadcast(firstAttack, secondAttack) } attackValueResult := f.buildNoteUseSkillOutboundInfo() //因为切完才能广播,所以必须和回合结束分开结算 f.Broadcast(func(fighter *input.Input) { for _, switchAction := range f.Switch { if fighter.Player.GetInfo().UserID != switchAction.Reason.UserId { // println("切精灵", switchAction.Reason.UserId, switchAction.Reason.ID) fighter.Player.SendPackCmd(2407, &switchAction.Reason) } } }) f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction) if f.closefight && f.Info.Mode == info.BattleMode.PET_MELEE { // f.Broadcast(func(fighter *input.Input) { // if fighter.UserID != f.WinnerId { // fighter.Player.SendPackCmd(2505, &attackValueResult) // } // }) return } f.Broadcast(func(fighter *input.Input) { fighter.Player.SendPackCmd(2505, &attackValueResult) fighter.CanChange = 0 }) if f.closefight { return } } func (f *FightC) TURNOVER(cur *input.Input) { var _hasBackup bool if cur == nil { return } for _, pet := range cur.BenchPets() { if pet != nil && pet.Info.Hp > 0 { _hasBackup = true break } } f.sendLegacySpriteDie(cur, _hasBackup) f.Broadcast(func(ff *input.Input) { ff.Exec(func(t input.Effect) bool { t.SwitchOut(cur) return true }) }) if f.IsWin(f.GetInputByPlayer(cur.Player, true)) { //然后检查是否战斗结束 f.FightOverInfo.WinnerId = f.GetInputByPlayer(cur.Player, true).UserID f.FightOverInfo.Reason = model.BattleOverReason.DefaultEnd f.WinnerId = f.FightOverInfo.WinnerId f.Reason = model.BattleOverReason.DefaultEnd f.closefight = true // break } }