新增了多个宠物战斗中的技能效果实现,包括伤害反弹、必杀技增强、先手权调整、 回复机制以及特殊条件触发逻辑。同时修复了部分技能判断条件与执行顺序问题, 优化了 AI 在 NPC 战斗中的行为表现,并完善了相关配置文件内容。
380 lines
9.7 KiB
Go
380 lines
9.7 KiB
Go
package fight
|
||
|
||
import (
|
||
"blazing/common/utils"
|
||
|
||
"blazing/logic/service/fight/action"
|
||
"blazing/logic/service/fight/info"
|
||
"blazing/logic/service/fight/input"
|
||
"blazing/logic/service/player"
|
||
"blazing/modules/blazing/model"
|
||
"fmt"
|
||
"reflect"
|
||
|
||
"github.com/barkimedes/go-deepcopy"
|
||
"github.com/shopspring/decimal"
|
||
)
|
||
|
||
// 处理技能攻击逻辑
|
||
func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *info.SkillEntity) {
|
||
//oldpet := f.copypet(attacker.CurrentPet)
|
||
|
||
a.AttackTimeC(attacker.GetProp(5, true)) //计算命中
|
||
defender.Exec(func(t input.Effect) bool { //计算闪避 ,然后修改对方命中),同时相当于计算属性无效这种
|
||
|
||
t.Ctx().SkillEntity = a
|
||
t.Skill_Hit_ex()
|
||
|
||
return true
|
||
})
|
||
|
||
attacker.AttackValue.AttackTime = a.AttackTime //是否命中赋值
|
||
var oldprop [2][6]int8
|
||
oldprop[0], oldprop[1] = attacker.Prop, defender.Prop //先复制能力提升
|
||
attacker.Exec(func(t input.Effect) bool {
|
||
//计算变威力
|
||
t.Ctx().SkillEntity = a
|
||
t.Skill_Hit() //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
|
||
|
||
return true
|
||
})
|
||
//技能命中+效果失效 这里就是修改效果命中为false
|
||
//技能miss+效果生效 这里属于强制改命中效果,但是正常来说,技能miss掉后效果也应该失效
|
||
//技能失效+效果失效
|
||
// 记录技能信息
|
||
attacker.SkillID = uint32(a.ID) //获取技能ID
|
||
if attacker.AttackTime > 0 { //如果命中
|
||
|
||
attacker.CalculateCrit(defender, a) //暴击计算
|
||
attacker.IsCritical = a.Crit
|
||
|
||
attacker.SumDamage = attacker.CalculatePower(defender, a)
|
||
//睡眠受击消除
|
||
|
||
}
|
||
attacker.Prop, defender.Prop = oldprop[0], oldprop[1] //先复制能力提升
|
||
|
||
if attacker.IsCritical == 1 { //命中了才有暴击
|
||
//暴击破防
|
||
if a.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 {
|
||
|
||
defender.Prop[1] = 0
|
||
} else if a.Category() == info.Category.SPECIAL && defender.Prop[3] > 0 {
|
||
defender.Prop[3] = 0
|
||
}
|
||
|
||
//暴击翻倍
|
||
attacker.SumDamage = attacker.SumDamage.Mul(decimal.NewFromInt(2))
|
||
|
||
}
|
||
// attacker.AddEffects(attacker.EffectCache...) //命中再添加效果
|
||
for _, e := range attacker.EffectCache {
|
||
//这里实现应该参考本地技能是否命中,然后
|
||
e.Hit(attacker.AttackTime != 0) //我方效果命中
|
||
}
|
||
|
||
// 扣减防御方血量
|
||
attacker.Exec(func(t input.Effect) bool {
|
||
t.Ctx().SkillEntity = a
|
||
|
||
t.OnSkill() //调用伤害计算
|
||
|
||
return true
|
||
})
|
||
|
||
defender.Damage(attacker, &info.DamageZone{
|
||
|
||
Damage: attacker.SumDamage,
|
||
},
|
||
)
|
||
//这里其实是受到致死伤害
|
||
//然后先触发死亡效果,消除所有buff
|
||
//然后触发回神效果
|
||
}
|
||
func IsNil(x interface{}) bool {
|
||
if x == nil {
|
||
return true
|
||
}
|
||
rv := reflect.ValueOf(x)
|
||
return rv.Kind() == reflect.Ptr && rv.IsNil()
|
||
}
|
||
func (f *FightC) copyskill(t *action.SelectSkillAction) *info.SkillEntity {
|
||
if t == nil {
|
||
return nil
|
||
}
|
||
if t.SkillEntity == nil {
|
||
return nil
|
||
|
||
}
|
||
|
||
oldskill, _ := deepcopy.Anything(t.SkillEntity) //备份技能
|
||
oldskill.(*info.SkillEntity).Rand = f.rand //拷贝后随机数丢失
|
||
return oldskill.(*info.SkillEntity)
|
||
}
|
||
|
||
//回合有先手方和后手方,同时有攻击方和被攻击方
|
||
|
||
func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
|
||
//双方首发精灵登场时,依挑战方机制:房主方先判定,挑战方后判定
|
||
//双方非首发精灵登场时,根据切换先后判定(看手速)
|
||
// 神罗、圣华登场时魂免,“登场时xx”等效果
|
||
//阿枫的效果也在这里判断
|
||
|
||
f.Broadcast(func(ff *input.Input) {
|
||
ff.Exec(func(t input.Effect) bool { //回合开始前
|
||
|
||
//结算状态
|
||
t.Turn_Start(fattack, sattack)
|
||
return true
|
||
})
|
||
})
|
||
|
||
if fattack != nil { //如果首技能是空的,说明都空过了
|
||
|
||
if fattack.GetPlayerID() == f.ownerID {
|
||
//是否miss都应该施加解析effect
|
||
f.Our.Parseskill(fattack) //解析到临时数据
|
||
|
||
f.Opp.Parseskill(sattack) //解析到临时数据
|
||
} else {
|
||
f.Opp.Parseskill(fattack)
|
||
f.Our.Parseskill(sattack)
|
||
}
|
||
}
|
||
f.Broadcast(func(ff *input.Input) {
|
||
ff.Exec(func(t input.Effect) bool { //回合开始前
|
||
|
||
//结算状态
|
||
t.Compare_Pre(fattack, sattack) //先结算技能的优先级
|
||
return true
|
||
})
|
||
|
||
ff.ResetAttackValue()
|
||
})
|
||
f.First, f.Second = f.Our, f.Opp
|
||
// 根据攻击方归属设置当前战斗的主/次攻击方属性
|
||
if fattack != nil {
|
||
if fattack.GetPlayerID() != f.ownerID {
|
||
|
||
f.First, f.Second = f.Opp, f.Our // 攻击方为对方时,主攻击方是对方
|
||
|
||
}
|
||
}
|
||
|
||
if fattack != nil && sattack != nil {
|
||
switch {
|
||
|
||
case fattack.SkillEntity.Priority < sattack.SkillEntity.Priority:
|
||
|
||
fattack, sattack = sattack, fattack //互换先手权
|
||
f.First, f.Second = f.Second, f.First
|
||
case fattack.SkillEntity.Priority == sattack.SkillEntity.Priority:
|
||
|
||
if f.Second.GetProp(4, false) > f.First.GetProp(4, false) {
|
||
fattack, sattack = sattack, fattack //互换先手权
|
||
f.First, f.Second = f.Second, f.First
|
||
}
|
||
|
||
}
|
||
}
|
||
var attacker, defender *input.Input
|
||
|
||
//开始回合操作
|
||
for i := 0; i < 2; i++ {
|
||
var oldskill *info.SkillEntity //原始技能
|
||
var currentskill *info.SkillEntity //当前技能
|
||
if i == 0 { //
|
||
attacker, defender = f.First, f.Second
|
||
oldskill = f.copyskill(fattack)
|
||
|
||
} else {
|
||
attacker, defender = f.Second, f.First
|
||
oldskill = f.copyskill(sattack)
|
||
|
||
}
|
||
|
||
currentskill = oldskill
|
||
defender.Exec(func(t input.Effect) bool { //这个是能否使用技能
|
||
//结算状态
|
||
//然后这里还可以处理自爆类
|
||
t.Ctx().SkillEntity = currentskill
|
||
return t.Action_start_ex(fattack, sattack) //返回本身结算,如果false,说明不能使用技能了
|
||
|
||
})
|
||
canuseskill := attacker.Exec(func(t input.Effect) bool { //这个是能否使用技能
|
||
//结算状态
|
||
//然后这里还可以处理自爆类
|
||
t.Ctx().SkillEntity = currentskill
|
||
return t.Action_start(fattack, sattack) //返回本身结算,如果false,说明不能使用技能了
|
||
|
||
})
|
||
|
||
canuse := canuseskill && //
|
||
action.CanUse(currentskill) && //pp还在
|
||
attacker.CurrentPet.Info.Hp > 0
|
||
|
||
if !canuse {
|
||
//根本没释放技能,这些效果全部失效
|
||
for _, e := range attacker.EffectCache {
|
||
e.Alive(false)
|
||
|
||
}
|
||
|
||
//这时候将被覆盖的效果全部装回来enterturn
|
||
for _, e := range attacker.Effect_Lost {
|
||
e.Alive(true)
|
||
|
||
}
|
||
}
|
||
// 结算状态
|
||
// 然后这里还可以处理自爆类
|
||
if canuse { //可以使用技能
|
||
|
||
f.processSkillAttack(attacker, defender, currentskill)
|
||
currentskill = oldskill //还原技能
|
||
// if oldskill != nil {
|
||
// fmt.Println("结束攻击_old", oldskill.Power)
|
||
// fmt.Println("结束攻击_new", currentskill.Power)
|
||
// }
|
||
|
||
_, skill, ok := utils.FindWithIndex(attacker.CurrentPet.Info.SkillList, func(item model.SkillInfo) bool {
|
||
return item.ID == currentskill.Info.ID
|
||
})
|
||
if ok {
|
||
|
||
skill.PP--
|
||
}
|
||
|
||
}
|
||
//0血不触发
|
||
if defender.CurrentPet.Info.Hp > 0 {
|
||
//技能使用后
|
||
defender.Exec(func(t input.Effect) bool {
|
||
t.Ctx().SkillEntity = currentskill
|
||
|
||
t.Skill_Use_ex()
|
||
|
||
return true
|
||
})
|
||
}
|
||
|
||
//技能使用后
|
||
attacker.Exec(func(t input.Effect) bool { //技能使用后的我方效果
|
||
t.Ctx().SkillEntity = currentskill
|
||
|
||
t.Skill_Useed()
|
||
|
||
return true
|
||
})
|
||
defender.Exec(func(t input.Effect) bool {
|
||
t.Ctx().SkillEntity = currentskill
|
||
|
||
t.Action_end_ex()
|
||
|
||
return true
|
||
})
|
||
|
||
//技能使用后
|
||
attacker.Exec(func(t input.Effect) bool { //技能使用后的我方效果
|
||
t.Ctx().SkillEntity = currentskill
|
||
|
||
t.Action_end()
|
||
|
||
return true
|
||
})
|
||
fmt.Println(i,
|
||
// "玩家技能:", oldskill.(*info.SkillEntity).ID,
|
||
"玩家技能伤害:", attacker.SumDamage,
|
||
"自身剩余血量:", attacker.CurrentPet.Info.Hp,
|
||
"对手剩余血量:", defender.CurrentPet.Info.Hp,
|
||
)
|
||
if attacker.CurrentPet.Info.Hp <= 0 {
|
||
if defender.CurrentPet.Info.Hp == 0 { //先手方死亡,触发反同归于尽
|
||
|
||
defender.CurrentPet.Info.Hp = 1
|
||
}
|
||
if f.IsWin(defender, attacker.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
|
||
|
||
f.FightOverInfo.WinnerId = defender.UserID
|
||
|
||
f.closefight = true
|
||
// break
|
||
}
|
||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
|
||
if _, ok := defender.Player.(*player.AI_player); ok {
|
||
defender.GetAction(f.Our)
|
||
//panic("AI自动技能")
|
||
}
|
||
|
||
}
|
||
attacker.CanChange = true
|
||
break
|
||
}
|
||
|
||
if defender.CurrentPet.Info.Hp == 0 {
|
||
if attacker.CurrentPet.Info.Hp == 0 { //先手方死亡,触发反同归于尽
|
||
|
||
attacker.CurrentPet.Info.Hp = 1
|
||
}
|
||
|
||
defender.CanChange = true //被打死就可以切精灵了
|
||
// AI自动技能
|
||
|
||
if f.IsWin(attacker, defender.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
|
||
var WinnerId uint32
|
||
if i == 0 {
|
||
WinnerId = f.First.Player.GetInfo().UserID
|
||
} else {
|
||
WinnerId = f.Second.Player.GetInfo().UserID
|
||
}
|
||
f.FightOverInfo.WinnerId = WinnerId
|
||
|
||
f.closefight = true
|
||
|
||
} else {
|
||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
|
||
if _, ok := defender.Player.(*player.AI_player); ok {
|
||
defender.GetAction(f.Our)
|
||
//panic("AI自动技能")
|
||
}
|
||
|
||
}
|
||
}
|
||
break
|
||
}
|
||
|
||
}
|
||
|
||
f.Broadcast(func(ff *input.Input) {
|
||
ff.GenSataus()
|
||
ff.Exec(func(t input.Effect) bool { //这个是能否使用技能
|
||
//结算状态
|
||
t.Turn_End() //返回本身结算,如果false,说明不能使用技能了
|
||
return true
|
||
})
|
||
ff.GenInfo()
|
||
})
|
||
|
||
ret := info.AttackValueS{
|
||
|
||
FAttack: *f.First.AttackValue,
|
||
SAttack: *f.Second.AttackValue,
|
||
}
|
||
//因为切完才能广播,所以必须和回合结束分开结算
|
||
f.Broadcast(func(ff *input.Input) {
|
||
for _, v := range f.Switch {
|
||
|
||
if ff.Player.GetInfo().UserID != v.Reason.UserId {
|
||
|
||
ff.Player.SendPackCmd(2407, &v.Reason)
|
||
}
|
||
|
||
}
|
||
|
||
})
|
||
f.Switch = []*action.ActiveSwitchAction{}
|
||
f.Broadcast(func(ff *input.Input) {
|
||
ff.Player.SendPackCmd(2505, &ret)
|
||
})
|
||
}
|