2025-09-14 01:35:16 +08:00
|
|
|
|
package fight
|
2025-09-04 02:00:57 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2025-10-27 01:11:31 +08:00
|
|
|
|
"blazing/common/utils"
|
2025-11-15 22:17:43 +00:00
|
|
|
|
|
2026-04-09 02:14:09 +08:00
|
|
|
|
"blazing/logic/service/common"
|
2025-09-28 08:13:42 +00:00
|
|
|
|
"blazing/logic/service/fight/action"
|
2025-09-04 02:00:57 +08:00
|
|
|
|
"blazing/logic/service/fight/info"
|
2025-09-14 01:35:16 +08:00
|
|
|
|
"blazing/logic/service/fight/input"
|
2026-01-19 18:51:56 +08:00
|
|
|
|
"blazing/modules/player/model"
|
2025-10-10 00:40:32 +08:00
|
|
|
|
"reflect"
|
2025-09-04 02:00:57 +08:00
|
|
|
|
|
2025-12-05 00:24:02 +08:00
|
|
|
|
"github.com/alpacahq/alpacadecimal"
|
2025-09-26 21:15:58 +00:00
|
|
|
|
"github.com/barkimedes/go-deepcopy"
|
2025-12-10 18:37:32 +00:00
|
|
|
|
"github.com/gogf/gf/v2/util/grand"
|
2025-09-04 02:00:57 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// processSkillAttack 处理技能攻击逻辑
|
2026-02-03 19:23:37 +08:00
|
|
|
|
func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info.SkillEntity) {
|
2026-04-04 05:44:02 +08:00
|
|
|
|
if attacker == nil || defender == nil || skill == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-03-09 19:16:17 +08:00
|
|
|
|
skill.AttackTimeC(attacker.Prop[5]) //计算命中
|
2025-09-14 16:56:31 +08:00
|
|
|
|
|
2026-04-04 06:27:15 +08:00
|
|
|
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //计算闪避,然后修改对方命中),同时相当于计算属性无效这种
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, skill, defender)
|
2026-01-04 22:10:34 +08:00
|
|
|
|
effect.SkillHit_ex()
|
2025-09-24 18:53:54 +00:00
|
|
|
|
return true
|
2025-09-12 00:27:49 +08:00
|
|
|
|
})
|
2025-09-14 03:36:26 +08:00
|
|
|
|
|
2026-04-04 06:27:15 +08:00
|
|
|
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
|
2026-01-05 00:54:52 +08:00
|
|
|
|
//计算变威力
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, skill, defender)
|
2026-01-05 00:54:52 +08:00
|
|
|
|
effect.SkillHit() //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
|
|
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-12-24 19:03:11 +08:00
|
|
|
|
var originalProps [2][6]int8
|
|
|
|
|
|
var originalPetInfo [2]model.PetInfo
|
2026-04-04 22:13:42 +08:00
|
|
|
|
attackerPet := attacker.CurrentPet()
|
|
|
|
|
|
defenderPet := defender.CurrentPet()
|
|
|
|
|
|
if attackerPet == nil || defenderPet == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-02-03 19:23:37 +08:00
|
|
|
|
//复制属性
|
2026-04-04 05:44:02 +08:00
|
|
|
|
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
|
2026-04-04 22:13:42 +08:00
|
|
|
|
originalPetInfo[0], originalPetInfo[1] = attackerPet.Info, defenderPet.Info
|
2026-04-04 06:27:15 +08:00
|
|
|
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
|
2025-11-11 05:54:24 +00:00
|
|
|
|
//计算变威力
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, skill, defender)
|
2026-01-05 00:54:52 +08:00
|
|
|
|
effect.CalculatePre() //相当于先调整基础命中,不光调整命中,这里还能调整技能属性,暴击率
|
2025-10-05 00:29:22 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
//技能命中+效果失效 这里就是修改效果命中为false
|
|
|
|
|
|
//技能miss+效果生效 这里属于强制改命中效果,但是正常来说,技能miss掉后效果也应该失效
|
|
|
|
|
|
//技能失效+效果失效
|
2025-12-24 19:03:11 +08:00
|
|
|
|
attacker.AttackTime = skill.AttackTime
|
2026-03-09 18:49:51 +08:00
|
|
|
|
attacker.SkillID = uint32(skill.XML.ID) //获取技能ID
|
2026-03-09 22:36:30 +08:00
|
|
|
|
var SumDamage alpacadecimal.Decimal
|
|
|
|
|
|
if skill.AttackTime != 0 { //如果命中
|
2026-03-10 00:06:02 +08:00
|
|
|
|
|
|
|
|
|
|
SumDamage = attacker.CalculatePower(defender, skill)
|
2025-12-24 19:03:11 +08:00
|
|
|
|
attacker.CalculateCrit(defender, skill) //暴击计算
|
|
|
|
|
|
attacker.IsCritical = skill.Crit
|
2025-11-20 05:57:29 +08:00
|
|
|
|
}
|
2025-11-30 10:21:57 +00:00
|
|
|
|
|
2026-02-03 19:23:37 +08:00
|
|
|
|
//还原属性
|
2026-04-04 05:44:02 +08:00
|
|
|
|
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
|
2026-04-04 22:13:42 +08:00
|
|
|
|
attackerPet.Info, defenderPet.Info = originalPetInfo[0], originalPetInfo[1]
|
2026-02-03 00:35:00 +08:00
|
|
|
|
|
2025-11-20 05:57:29 +08:00
|
|
|
|
if attacker.IsCritical == 1 { //命中了才有暴击
|
|
|
|
|
|
//暴击破防
|
2025-12-24 19:03:11 +08:00
|
|
|
|
if skill.Category() == info.Category.PHYSICAL && defender.Prop[1] > 0 {
|
2025-11-20 05:57:29 +08:00
|
|
|
|
defender.Prop[1] = 0
|
2025-12-24 19:03:11 +08:00
|
|
|
|
} else if skill.Category() == info.Category.SPECIAL && defender.Prop[3] > 0 {
|
2025-11-20 05:57:29 +08:00
|
|
|
|
defender.Prop[3] = 0
|
2025-09-26 02:09:33 +00:00
|
|
|
|
}
|
2025-11-20 05:57:29 +08:00
|
|
|
|
//暴击翻倍
|
2026-03-09 22:36:30 +08:00
|
|
|
|
SumDamage = SumDamage.Mul(alpacadecimal.NewFromInt(2))
|
2025-10-05 00:29:22 +08:00
|
|
|
|
}
|
2025-11-30 10:21:57 +00:00
|
|
|
|
|
2026-01-04 21:41:10 +08:00
|
|
|
|
//到这里已经是强制miss或者命中,所以根本不存在强制miss改命中的情况,因为miss的时候不会执行到这里
|
|
|
|
|
|
if !skill.Hit {
|
|
|
|
|
|
|
|
|
|
|
|
for _, effect := range attacker.EffectCache {
|
|
|
|
|
|
effect.Alive(false) //我方效果命中
|
|
|
|
|
|
}
|
2025-11-30 10:21:57 +00:00
|
|
|
|
//这时候将被覆盖的效果全部装回来enterturn
|
2026-01-04 21:41:10 +08:00
|
|
|
|
for _, effect := range attacker.EffectLost {
|
2025-12-24 19:03:11 +08:00
|
|
|
|
if effect.Duration() > 0 || effect.Duration() == -1 {
|
2026-01-04 21:41:10 +08:00
|
|
|
|
//effect.Alive(true)
|
|
|
|
|
|
attacker.AddEffect(effect.GetInput(), effect)
|
2025-11-30 10:21:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-05 00:29:22 +08:00
|
|
|
|
}
|
2025-09-08 01:23:12 +08:00
|
|
|
|
|
2025-10-05 00:29:22 +08:00
|
|
|
|
// 扣减防御方血量
|
2026-04-04 06:27:15 +08:00
|
|
|
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, skill, defender)
|
2025-12-24 19:03:11 +08:00
|
|
|
|
effect.OnSkill() //调用伤害计算
|
2025-09-26 21:15:58 +00:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-09-29 02:40:35 +08:00
|
|
|
|
|
2025-11-12 13:44:21 +00:00
|
|
|
|
defender.Damage(attacker, &info.DamageZone{
|
2026-03-09 22:36:30 +08:00
|
|
|
|
Damage: SumDamage,
|
|
|
|
|
|
Type: info.DamageType.Red,
|
2025-12-24 19:03:11 +08:00
|
|
|
|
})
|
2025-09-08 02:51:21 +00:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
|
|
|
|
|
// IsNil 检查值是否为nil
|
2025-10-10 00:40:32 +08:00
|
|
|
|
func IsNil(x interface{}) bool {
|
|
|
|
|
|
if x == nil {
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
rv := reflect.ValueOf(x)
|
|
|
|
|
|
return rv.Kind() == reflect.Ptr && rv.IsNil()
|
|
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
|
|
|
|
|
// copySkill 复制技能实体
|
|
|
|
|
|
func (f *FightC) copySkill(action *action.SelectSkillAction) *info.SkillEntity {
|
|
|
|
|
|
if action == nil {
|
2025-11-10 01:53:28 +00:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
if action.SkillEntity == nil {
|
2025-11-10 03:23:32 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2025-10-26 20:56:03 +08:00
|
|
|
|
|
2026-03-09 19:16:17 +08:00
|
|
|
|
originalSkill, _ := deepcopy.Anything(action.SkillEntity) //备份技能
|
|
|
|
|
|
originalSkill.(*info.SkillEntity).Accuracy = action.SkillEntity.Accuracy //拷贝后命中丢失
|
2025-12-24 19:03:11 +08:00
|
|
|
|
return originalSkill.(*info.SkillEntity)
|
2025-10-26 20:56:03 +08:00
|
|
|
|
}
|
2025-09-10 22:59:10 +08:00
|
|
|
|
|
2026-04-04 04:28:04 +08:00
|
|
|
|
func (f *FightC) getSkillParticipants(skillAction *action.SelectSkillAction) (*input.Input, *input.Input) {
|
|
|
|
|
|
if skillAction == nil {
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
|
}
|
2026-04-17 00:48:43 +08:00
|
|
|
|
attacker := f.GetInputByAction(skillAction, false)
|
|
|
|
|
|
defender := f.GetInputByAction(skillAction, true)
|
|
|
|
|
|
if attacker != nil && defender == attacker && shouldResolveOpponentAsTarget(skillAction.SkillEntity) {
|
|
|
|
|
|
if opponent, _ := attacker.OpponentSlotAtOrNextLiving(0); opponent != nil {
|
|
|
|
|
|
defender = opponent
|
|
|
|
|
|
} else if opponent := f.roundOpponentInput(attacker); opponent != nil {
|
|
|
|
|
|
defender = opponent
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return attacker, defender
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func shouldResolveOpponentAsTarget(skill *info.SkillEntity) bool {
|
|
|
|
|
|
return skill != nil && skill.XML.AtkType == 3
|
2026-04-04 04:28:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 02:25:44 +08:00
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-04 22:13:42 +08:00
|
|
|
|
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
|
2026-04-13 11:28:30 +08:00
|
|
|
|
if attackValue.SkillID == 0 {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-04-04 22:13:42 +08:00
|
|
|
|
attackValue.ActorIndex = uint32(actorIndex)
|
|
|
|
|
|
values = append(values, attackValue)
|
|
|
|
|
|
}
|
|
|
|
|
|
return values
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-13 22:53:02 +08:00
|
|
|
|
func (f *FightC) buildAttackValueForBroadcast(fighter *input.Input, fallbackActorIndex int) model.AttackValue {
|
|
|
|
|
|
if fighter == nil {
|
|
|
|
|
|
return model.AttackValue{}
|
2026-04-13 22:27:27 +08:00
|
|
|
|
}
|
2026-04-13 22:53:02 +08:00
|
|
|
|
if fighter.AttackValue == nil {
|
|
|
|
|
|
empty := info.NewAttackValue(fighter.UserID)
|
|
|
|
|
|
fighter.AttackValue = empty
|
2026-04-13 22:27:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
attackValue := *fighter.AttackValue
|
2026-04-13 22:53:02 +08:00
|
|
|
|
attackValue.ActorIndex = uint32(fallbackActorIndex)
|
|
|
|
|
|
if attackValue.UserID == 0 && fighter.Player != nil && fighter.Player.GetInfo() != nil {
|
|
|
|
|
|
attackValue.UserID = fighter.Player.GetInfo().UserID
|
|
|
|
|
|
}
|
|
|
|
|
|
return attackValue
|
2026-04-13 22:27:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-13 22:53:02 +08:00
|
|
|
|
func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo {
|
2026-04-04 22:13:42 +08:00
|
|
|
|
result := info.NoteUseSkillOutboundInfo{}
|
2026-04-13 22:53:02 +08:00
|
|
|
|
if f.First != nil {
|
|
|
|
|
|
result.FirstAttackInfo = f.buildAttackValueForBroadcast(f.First, f.First.TeamSlotIndex())
|
|
|
|
|
|
}
|
|
|
|
|
|
if f.Second != nil {
|
|
|
|
|
|
result.SecondAttackInfo = f.buildAttackValueForBroadcast(f.Second, f.Second.TeamSlotIndex())
|
|
|
|
|
|
}
|
2026-04-04 22:13:42 +08:00
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 22:44:13 +08:00
|
|
|
|
func (f *FightC) roundOpponentInput(attacker *input.Input) *input.Input {
|
|
|
|
|
|
if attacker == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, opponent := range attacker.OpponentSlots() {
|
|
|
|
|
|
if opponent != nil {
|
|
|
|
|
|
return opponent
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-17 00:48:43 +08:00
|
|
|
|
func shouldSkipSecondAction(first, second *input.Input) bool {
|
2026-04-16 23:49:28 +08:00
|
|
|
|
if first == nil || second == nil {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
firstPet := first.CurrentPet()
|
|
|
|
|
|
secondPet := second.CurrentPet()
|
|
|
|
|
|
return firstPet == nil || firstPet.Info.Hp <= 0 || secondPet == nil || secondPet.Info.Hp <= 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// enterturn 处理战斗回合逻辑
|
|
|
|
|
|
// 回合有先手方和后手方,同时有攻击方和被攻击方
|
|
|
|
|
|
func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) {
|
2025-11-13 21:36:18 +08:00
|
|
|
|
//双方首发精灵登场时,依挑战方机制:房主方先判定,挑战方后判定
|
|
|
|
|
|
//双方非首发精灵登场时,根据切换先后判定(看手速)
|
|
|
|
|
|
// 神罗、圣华登场时魂免,“登场时xx”等效果
|
|
|
|
|
|
//阿枫的效果也在这里判断
|
2025-09-14 03:36:26 +08:00
|
|
|
|
|
2025-12-03 22:05:28 +08:00
|
|
|
|
if f.closefight {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-16 20:30:17 +00:00
|
|
|
|
f.Broadcast(func(ff *input.Input) {
|
2026-01-03 01:35:32 +08:00
|
|
|
|
|
|
|
|
|
|
ff.EffectCache = make([]input.Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来
|
2026-01-04 21:41:10 +08:00
|
|
|
|
ff.EffectLost = make([]input.Effect, 0)
|
2026-04-05 02:25:44 +08:00
|
|
|
|
opponent := f.plannedOpponentForCarrier(ff, firstAttack, secondAttack)
|
|
|
|
|
|
ff.ExecWithOpponent(opponent, func(effect input.Effect) bool { //回合开始前
|
|
|
|
|
|
f.setEffectTarget(effect, opponent)
|
2026-01-05 22:54:41 +08:00
|
|
|
|
effect.TurnStart(firstAttack, secondAttack)
|
2025-11-16 20:30:17 +00:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-11-13 21:36:18 +08:00
|
|
|
|
})
|
2025-09-30 18:32:15 +08:00
|
|
|
|
|
2026-04-04 05:44:02 +08:00
|
|
|
|
for _, skillAction := range []*action.SelectSkillAction{firstAttack, secondAttack} {
|
|
|
|
|
|
attackerInput, _ := f.getSkillParticipants(skillAction)
|
|
|
|
|
|
if attackerInput == nil {
|
|
|
|
|
|
continue
|
2025-11-14 06:14:49 +08:00
|
|
|
|
}
|
2026-04-04 05:44:02 +08:00
|
|
|
|
f.setActionAttackValue(skillAction)
|
|
|
|
|
|
attackerInput.Parseskill(skillAction)
|
2025-11-14 06:14:49 +08:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
f.Broadcast(func(fighter *input.Input) {
|
2026-04-05 02:25:44 +08:00
|
|
|
|
opponent := f.plannedOpponentForCarrier(fighter, firstAttack, secondAttack)
|
|
|
|
|
|
fighter.ExecWithOpponent(opponent, func(effect input.Effect) bool { //回合开始前
|
|
|
|
|
|
f.setEffectTarget(effect, opponent)
|
2026-01-04 22:10:34 +08:00
|
|
|
|
effect.ComparePre(firstAttack, secondAttack) //先结算技能的优先级
|
2025-11-16 20:30:17 +00:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-12-24 19:03:11 +08:00
|
|
|
|
fighter.ResetAttackValue()
|
2025-11-14 06:14:49 +08:00
|
|
|
|
})
|
2026-04-04 04:28:04 +08:00
|
|
|
|
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:
|
2026-04-12 22:44:13 +08:00
|
|
|
|
f.First, _ = f.getSkillParticipants(firstAttack)
|
|
|
|
|
|
f.Second = f.roundOpponentInput(f.First)
|
2026-04-04 04:28:04 +08:00
|
|
|
|
case secondAttack != nil:
|
2026-04-12 22:44:13 +08:00
|
|
|
|
f.First, _ = f.getSkillParticipants(secondAttack)
|
|
|
|
|
|
f.Second = f.roundOpponentInput(f.First)
|
2026-04-04 04:28:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
if f.First == nil {
|
|
|
|
|
|
f.First = f.primaryOur()
|
|
|
|
|
|
}
|
|
|
|
|
|
if f.Second == nil {
|
|
|
|
|
|
f.Second = f.primaryOpp()
|
2025-09-30 18:32:15 +08:00
|
|
|
|
}
|
2025-10-29 15:50:48 +00:00
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
if firstAttack != nil && secondAttack != nil {
|
2025-11-08 00:47:45 +08:00
|
|
|
|
switch {
|
2026-03-09 18:49:51 +08:00
|
|
|
|
case firstAttack.SkillEntity.XML.Priority < secondAttack.SkillEntity.XML.Priority:
|
2025-12-24 19:03:11 +08:00
|
|
|
|
firstAttack, secondAttack = secondAttack, firstAttack //互换先手权
|
2025-11-08 00:47:45 +08:00
|
|
|
|
f.First, f.Second = f.Second, f.First
|
2026-03-09 18:49:51 +08:00
|
|
|
|
case firstAttack.SkillEntity.XML.Priority == secondAttack.SkillEntity.XML.Priority:
|
2026-03-09 20:55:04 +08:00
|
|
|
|
if f.Second.GetProp(4).Cmp(f.First.GetProp(4)) > 0 {
|
2025-12-24 19:03:11 +08:00
|
|
|
|
firstAttack, secondAttack = secondAttack, firstAttack //互换先手权
|
2025-10-10 00:40:32 +08:00
|
|
|
|
f.First, f.Second = f.Second, f.First
|
|
|
|
|
|
}
|
2026-03-09 20:55:04 +08:00
|
|
|
|
if f.Second.GetProp(4).Cmp(f.First.GetProp(4)) == 0 {
|
2025-12-10 18:37:32 +00:00
|
|
|
|
if grand.Meet(1, 2) { //随机出手
|
2025-12-24 19:03:11 +08:00
|
|
|
|
firstAttack, secondAttack = secondAttack, firstAttack //互换先手权
|
2025-12-10 18:37:32 +00:00
|
|
|
|
f.First, f.Second = f.Second, f.First
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-30 18:32:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-10 18:37:32 +00:00
|
|
|
|
|
2026-04-13 21:06:45 +08:00
|
|
|
|
skipActionStage := firstAttack == nil && secondAttack == nil
|
2025-09-30 10:40:36 +00:00
|
|
|
|
var attacker, defender *input.Input
|
2026-03-09 20:55:04 +08:00
|
|
|
|
f.TrueFirst = f.First
|
2026-04-13 21:06:45 +08:00
|
|
|
|
//开始回合操作。若双方本回合都未出手,则只走完整回合流程,不进入动作阶段。
|
|
|
|
|
|
for i := 0; !skipActionStage && i < 2; i++ {
|
2025-12-24 19:03:11 +08:00
|
|
|
|
var originalSkill *info.SkillEntity //原始技能
|
|
|
|
|
|
var currentSkill *info.SkillEntity //当前技能
|
2026-04-04 05:44:02 +08:00
|
|
|
|
var currentAction *action.SelectSkillAction
|
2025-12-24 19:03:11 +08:00
|
|
|
|
if i == 0 {
|
2026-04-04 05:44:02 +08:00
|
|
|
|
currentAction = firstAttack
|
2026-04-13 22:27:27 +08:00
|
|
|
|
if currentAction == nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-04-04 05:44:02 +08:00
|
|
|
|
attacker, defender = f.getSkillParticipants(firstAttack)
|
2025-12-24 19:03:11 +08:00
|
|
|
|
originalSkill = f.copySkill(firstAttack)
|
2025-12-18 03:54:45 +00:00
|
|
|
|
//先手阶段,先修复后手效果
|
|
|
|
|
|
f.Second.RecoverEffect()
|
2025-09-30 10:40:36 +00:00
|
|
|
|
} else {
|
2026-04-04 05:44:02 +08:00
|
|
|
|
currentAction = secondAttack
|
2026-04-13 22:27:27 +08:00
|
|
|
|
if currentAction == nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-04-17 00:48:43 +08:00
|
|
|
|
if shouldSkipSecondAction(f.First, f.Second) {
|
2026-04-16 23:49:28 +08:00
|
|
|
|
secondAttack = nil
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-04-04 05:44:02 +08:00
|
|
|
|
attacker, defender = f.getSkillParticipants(secondAttack)
|
2025-12-24 19:03:11 +08:00
|
|
|
|
originalSkill = f.copySkill(secondAttack)
|
2026-03-08 22:43:51 +08:00
|
|
|
|
//取消后手历史效果
|
2025-12-18 03:54:45 +00:00
|
|
|
|
f.Second.ReactvieEffect()
|
2025-09-10 01:35:08 +08:00
|
|
|
|
}
|
2026-04-04 05:44:02 +08:00
|
|
|
|
if attacker == nil {
|
|
|
|
|
|
attacker = f.First
|
|
|
|
|
|
}
|
|
|
|
|
|
if defender == nil {
|
|
|
|
|
|
defender = f.Second
|
|
|
|
|
|
}
|
2025-09-14 04:48:38 +08:00
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
currentSkill = originalSkill
|
2026-04-04 22:13:42 +08:00
|
|
|
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //这个是能否使用技能
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-04-04 22:13:42 +08:00
|
|
|
|
return effect.ActionStartEx(firstAttack, secondAttack)
|
|
|
|
|
|
})
|
|
|
|
|
|
canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-04-04 22:13:42 +08:00
|
|
|
|
return effect.ActionStart(firstAttack, secondAttack)
|
|
|
|
|
|
})
|
2025-11-13 21:36:18 +08:00
|
|
|
|
|
2026-04-04 22:13:42 +08:00
|
|
|
|
attackerPet := attacker.CurrentPet()
|
|
|
|
|
|
defenderPet := defender.CurrentPet()
|
|
|
|
|
|
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attackerPet != nil && attackerPet.Info.Hp > 0
|
2025-11-15 13:20:42 +08:00
|
|
|
|
|
2025-12-31 03:16:58 +08:00
|
|
|
|
if !canUse {
|
|
|
|
|
|
|
2025-12-18 03:54:45 +00:00
|
|
|
|
attacker.RecoverEffect()
|
2025-12-24 19:03:11 +08:00
|
|
|
|
currentSkill = nil
|
2026-03-09 12:28:37 +08:00
|
|
|
|
if i == 0 { //先手方被控,这时候应该算做未出手状态
|
2026-03-09 17:14:41 +08:00
|
|
|
|
f.TrueFirst = defender
|
|
|
|
|
|
for _, effect := range defender.EffectCache {
|
|
|
|
|
|
effect.IsFirst(true)
|
2026-03-09 12:28:37 +08:00
|
|
|
|
}
|
2026-03-08 22:43:51 +08:00
|
|
|
|
|
2026-03-07 20:18:23 +08:00
|
|
|
|
}
|
2026-03-09 12:28:37 +08:00
|
|
|
|
//先手权不一定出手
|
2025-11-30 10:21:57 +00:00
|
|
|
|
} else {
|
2026-04-04 05:44:02 +08:00
|
|
|
|
f.setActionAttackValue(currentAction)
|
2026-03-09 17:14:41 +08:00
|
|
|
|
|
|
|
|
|
|
for _, effect := range attacker.EffectCache {
|
|
|
|
|
|
effect.IsFirst(true)
|
|
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
f.processSkillAttack(attacker, defender, currentSkill)
|
|
|
|
|
|
currentSkill = originalSkill //还原技能
|
2025-11-12 01:19:24 +08:00
|
|
|
|
|
2026-04-04 22:13:42 +08:00
|
|
|
|
_, skill, ok := utils.FindWithIndex(attackerPet.Info.SkillList, func(item model.SkillInfo) bool {
|
2025-12-24 19:03:11 +08:00
|
|
|
|
return item.ID == currentSkill.Info.ID
|
2025-10-27 01:11:31 +08:00
|
|
|
|
})
|
|
|
|
|
|
if ok {
|
2026-03-09 23:44:09 +08:00
|
|
|
|
usecount := 1
|
2026-04-04 06:27:15 +08:00
|
|
|
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-03-09 23:44:09 +08:00
|
|
|
|
effect.HookPP(&usecount)
|
|
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
skill.Use(usecount)
|
2025-10-27 01:11:31 +08:00
|
|
|
|
}
|
2025-09-10 02:16:47 +00:00
|
|
|
|
}
|
2026-04-04 22:13:42 +08:00
|
|
|
|
if defenderPet != nil && defenderPet.Info.Hp > 0 {
|
2025-12-01 23:31:48 +08:00
|
|
|
|
//技能使用后
|
2026-04-04 06:27:15 +08:00
|
|
|
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-03-09 17:42:52 +08:00
|
|
|
|
effect.Skill_Use_ex()
|
2025-12-01 23:31:48 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-11-26 15:25:10 +08:00
|
|
|
|
|
2026-04-04 22:13:42 +08:00
|
|
|
|
if attackerPet != nil && attackerPet.Info.Hp > 0 {
|
2026-03-07 20:18:23 +08:00
|
|
|
|
//技能使用后
|
2026-04-04 06:27:15 +08:00
|
|
|
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-03-09 17:42:52 +08:00
|
|
|
|
effect.Skill_Use()
|
2026-03-07 20:18:23 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-09-07 00:23:28 +08:00
|
|
|
|
}
|
2026-03-08 19:42:47 +08:00
|
|
|
|
//技能使用后
|
2026-04-04 06:27:15 +08:00
|
|
|
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-03-10 09:17:26 +08:00
|
|
|
|
effect.Action_end_ex()
|
2026-03-08 19:42:47 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
|
|
|
|
|
//技能使用后
|
2026-04-04 06:27:15 +08:00
|
|
|
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
|
2026-04-05 02:25:44 +08:00
|
|
|
|
f.setEffectSkillContext(effect, currentSkill, defender)
|
2026-03-10 09:17:26 +08:00
|
|
|
|
effect.Action_end()
|
2026-03-08 19:42:47 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2026-03-10 09:17:26 +08:00
|
|
|
|
|
2026-04-16 10:25:56 +08:00
|
|
|
|
if defenderPet != nil && attackerPet != nil && defenderPet.Info.Hp <= 0 && attackerPet.Info.Hp <= 0 { //先手方死亡,触发反同归于尽
|
|
|
|
|
|
attackerPet.Info.Hp = 1
|
2026-03-09 20:55:04 +08:00
|
|
|
|
}
|
2026-04-04 22:13:42 +08:00
|
|
|
|
if defenderPet != nil && defenderPet.Info.Hp <= 0 {
|
2026-03-09 20:55:04 +08:00
|
|
|
|
|
|
|
|
|
|
f.TURNOVER(defender)
|
|
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
2026-04-04 22:13:42 +08:00
|
|
|
|
if attackerPet != nil && attackerPet.Info.Hp <= 0 {
|
2026-03-09 20:55:04 +08:00
|
|
|
|
f.TURNOVER(attacker)
|
|
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 03:14:43 +08:00
|
|
|
|
}
|
2025-09-23 19:11:03 +00:00
|
|
|
|
|
2025-11-11 05:54:24 +00:00
|
|
|
|
f.Broadcast(func(ff *input.Input) {
|
|
|
|
|
|
ff.GenSataus()
|
|
|
|
|
|
ff.Exec(func(t input.Effect) bool { //这个是能否使用技能
|
|
|
|
|
|
//结算状态
|
2026-01-05 22:54:41 +08:00
|
|
|
|
t.TurnEnd() //返回本身结算,如果false,说明不能使用技能了
|
2025-11-11 05:54:24 +00:00
|
|
|
|
return true
|
2025-09-29 02:40:35 +08:00
|
|
|
|
})
|
2025-11-11 05:54:24 +00:00
|
|
|
|
ff.GenInfo()
|
2026-03-29 16:38:34 +08:00
|
|
|
|
ff.SnapshotTurnProp()
|
2025-09-23 22:20:52 +00:00
|
|
|
|
})
|
2026-03-09 12:28:37 +08:00
|
|
|
|
if f.TrueFirst != f.First {
|
|
|
|
|
|
f.First, f.Second = f.Second, f.First
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-10-27 01:11:31 +08:00
|
|
|
|
|
2026-04-08 01:28:55 +08:00
|
|
|
|
if f.LegacyGroupProtocol {
|
|
|
|
|
|
f.sendLegacyRoundBroadcast(firstAttack, secondAttack)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-13 22:53:02 +08:00
|
|
|
|
attackValueResult := f.buildNoteUseSkillOutboundInfo()
|
2025-11-20 15:19:13 +08:00
|
|
|
|
//因为切完才能广播,所以必须和回合结束分开结算
|
2026-04-09 02:14:09 +08:00
|
|
|
|
f.BroadcastPlayers(func(p common.PlayerI) {
|
2025-12-24 19:03:11 +08:00
|
|
|
|
for _, switchAction := range f.Switch {
|
2026-04-09 02:14:09 +08:00
|
|
|
|
if p.GetInfo().UserID != switchAction.Reason.UserId {
|
2026-01-25 03:40:29 +08:00
|
|
|
|
// println("切精灵", switchAction.Reason.UserId, switchAction.Reason.ID)
|
2026-04-09 02:14:09 +08:00
|
|
|
|
if f.LegacyGroupProtocol {
|
|
|
|
|
|
switchedInput := f.getInputByUserID(switchAction.Reason.UserId, int(switchAction.Reason.ActorIndex), false)
|
|
|
|
|
|
if switchedInput == nil {
|
|
|
|
|
|
switchedInput = f.getInputByUserID(switchAction.Reason.UserId, int(switchAction.ActorIndex), false)
|
|
|
|
|
|
}
|
|
|
|
|
|
f.sendLegacyGroupChangePetSuccess(p, switchedInput, &switchAction.Reason)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
f.sendFightPacket(p, fightPacketChangePetSuccess, &switchAction.Reason)
|
|
|
|
|
|
}
|
2025-09-19 06:58:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-08 01:23:12 +08:00
|
|
|
|
})
|
2026-04-04 05:44:02 +08:00
|
|
|
|
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
|
2026-03-08 18:43:56 +08:00
|
|
|
|
if f.closefight && f.Info.Mode == info.BattleMode.PET_MELEE {
|
2026-03-14 20:02:04 +08:00
|
|
|
|
// f.Broadcast(func(fighter *input.Input) {
|
|
|
|
|
|
// if fighter.UserID != f.WinnerId {
|
2026-04-08 12:26:37 +08:00
|
|
|
|
// f.sendFightPacket(fighter.Player, 2505, groupCmdSkillHurt, /&attackValueResult)
|
2026-03-14 20:02:04 +08:00
|
|
|
|
// }
|
2026-03-08 18:43:56 +08:00
|
|
|
|
|
2026-03-14 20:02:04 +08:00
|
|
|
|
// })
|
2026-03-08 18:43:56 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-04-13 22:53:02 +08:00
|
|
|
|
if attackValueResult.FirstAttackInfo.UserID != 0 || attackValueResult.SecondAttackInfo.UserID != 0 {
|
2026-04-13 11:28:30 +08:00
|
|
|
|
f.BroadcastPlayers(func(p common.PlayerI) {
|
|
|
|
|
|
if !f.LegacyGroupProtocol {
|
|
|
|
|
|
f.sendFightPacket(p, fightPacketSkillResult, &attackValueResult)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
f.Broadcast(func(fighter *input.Input) {
|
|
|
|
|
|
fighter.CanChange = 0
|
2025-11-20 15:19:13 +08:00
|
|
|
|
})
|
2026-01-03 02:18:31 +08:00
|
|
|
|
if f.closefight {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-01-07 02:30:21 +08:00
|
|
|
|
|
2025-09-04 03:14:43 +08:00
|
|
|
|
}
|
2026-03-07 18:50:51 +08:00
|
|
|
|
func (f *FightC) TURNOVER(cur *input.Input) {
|
2026-04-08 01:28:55 +08:00
|
|
|
|
var _hasBackup bool
|
|
|
|
|
|
if cur == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-04-11 09:39:00 +08:00
|
|
|
|
_hasBackup = cur.HasLivingBench()
|
2026-04-08 01:28:55 +08:00
|
|
|
|
f.sendLegacySpriteDie(cur, _hasBackup)
|
2026-03-28 21:57:22 +08:00
|
|
|
|
|
2026-03-07 18:50:51 +08:00
|
|
|
|
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
|
2026-04-14 00:38:50 +08:00
|
|
|
|
f.FightOverInfo.Reason = normalizeFightOverReason(model.BattleOverReason.DefaultEnd)
|
2026-04-08 01:28:55 +08:00
|
|
|
|
f.WinnerId = f.FightOverInfo.WinnerId
|
2026-04-14 00:38:50 +08:00
|
|
|
|
f.Reason = f.FightOverInfo.Reason
|
2026-03-07 18:50:51 +08:00
|
|
|
|
|
|
|
|
|
|
f.closefight = true
|
|
|
|
|
|
// break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|