Files
bl/logic/service/fight/fightc.go
2026-04-04 06:27:15 +08:00

397 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { //计算闪避,然后修改对方命中),同时相当于计算属性无效这种
effect.Ctx().SkillEntity = skill
effect.SkillHit_ex()
return true
})
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
//计算变威力
effect.Ctx().SkillEntity = skill
effect.SkillHit() //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
return true
})
var originalProps [2][6]int8
var originalPetInfo [2]model.PetInfo
//复制属性
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
originalPetInfo[0], originalPetInfo[1] = attacker.CurPet[0].Info, defender.CurPet[0].Info
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
//计算变威力
effect.Ctx().SkillEntity = skill
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]
attacker.CurPet[0].Info, defender.CurPet[0].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 {
effect.Ctx().SkillEntity = skill
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)
}
// 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)
ff.Exec(func(effect input.Effect) bool { //回合开始前
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) {
fighter.Exec(func(effect input.Effect) bool { //回合开始前
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 { //这个是能否使用技能
effect.Ctx().SkillEntity = currentSkill
return effect.ActionStartEx(firstAttack, secondAttack)
})
canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能
effect.Ctx().SkillEntity = currentSkill
return effect.ActionStart(firstAttack, secondAttack)
})
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attacker.CurPet[0].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(attacker.CurPet[0].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 { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
effect.HookPP(&usecount)
return true
})
skill.Use(usecount)
}
}
if defender.CurPet[0].Info.Hp > 0 {
//技能使用后
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
effect.Skill_Use_ex()
return true
})
}
if attacker.CurPet[0].Info.Hp > 0 {
//技能使用后
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
effect.Skill_Use()
return true
})
}
//技能使用后
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
effect.Action_end_ex()
return true
})
//技能使用后
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
effect.Ctx().SkillEntity = currentSkill
effect.Action_end()
return true
})
if defender.CurPet[0].Info.Hp <= 0 && attacker.CurPet[0].Info.Hp <= 0 { //先手方死亡,触发反同归于尽
attacker.CurPet[0].Info.Hp = 1
}
if defender.CurPet[0].Info.Hp <= 0 {
f.TURNOVER(defender)
break
}
if attacker.CurPet[0].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
}
attackValueResult := info.AttackValueS{
FAttack: *f.First.AttackValue,
SAttack: *f.Second.AttackValue,
}
//因为切完才能广播,所以必须和回合结束分开结算
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) {
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.closefight = true
// break
}
}