refactor(fight): 重构战斗系统

- 移除 NodeManager 相关代码,改为使用 input 包中的 Effect
- 重构 FightC 结构,添加 GetRand 方法
- 新增 BaseAction 结构和 NewBaseAction 函数
- 更新 effect 包中的 Effect 结构和相关方法
- 调整 BattleSkillEntity 中的 AttackTime 方法,增加 Hit 字段
- 更新 AttackValue 结构,保留原有的 AttackTime 字段
- 重构战斗逻辑,包括回合开始前的处理、技能使用、伤害计算等
This commit is contained in:
2025-09-14 03:36:26 +08:00
parent 06e8ae3d9c
commit 9d87ce9e46
19 changed files with 858 additions and 246 deletions

View File

@@ -0,0 +1,46 @@
package input
import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
)
type Input struct {
CanChange bool //是否可以死亡切换CanChange
CurrentPet *info.BattlePetEntity //当前精灵
AllPet []*info.BattlePetEntity
Player common.PlayerI
Finished bool //是否加载完成
*info.AttackValue
FightC common.FightI
// info.BattleActionI
Effect NodeManager //effects容器OurEffect node.NodeManager //effects容器
Damage decimal.Decimal //造成伤害
First bool
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {
return i.CurrentPet
}
func (i *Input) InitAttackValue() {
i.AttackValue = info.NewAttackValue(i.Player.ID())
}
func (i *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangePetInfo) {
for _, v := range i.AllPet {
if v.Info.CatchTime == uint32(id) {
copier.Copy(&Reason, &v.Info)
Reason.UserId = i.Player.ID()
ii = v
}
}
return
}

View File

@@ -0,0 +1,46 @@
package input
import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"github.com/jinzhu/copier"
"github.com/shopspring/decimal"
)
type Input struct {
CanChange bool //是否可以死亡切换CanChange
CurrentPet *info.BattlePetEntity //当前精灵
AllPet []*info.BattlePetEntity
Player common.PlayerI
Finished bool //是否加载完成
*info.AttackValue
FightC common.FightI
// info.BattleActionI
Effect NodeManager //effects容器OurEffect node.NodeManager //effects容器
Damage decimal.Decimal //造成伤害
First bool
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {
return i.CurrentPet
}
func (i *Input) InitAttackValue() {
i.AttackValue = info.NewAttackValue(i.Player.ID())
}
func (i *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangePetInfo) {
for _, v := range i.AllPet {
if v.Info.CatchTime == uint32(id) {
copier.Copy(&Reason, &v.Info)
Reason.UserId = i.Player.ID()
ii = v
}
}
return
}

View File

@@ -0,0 +1,218 @@
package input
import (
"blazing/logic/service/fight/info"
"reflect"
)
type Effect interface {
OnBattleStart() bool //战斗开始
OnTurnStart(attacker, defender *Input) bool //回合开始
UseSkill(attacker, defender *Input) bool //使用技能 可以取消用技能节点
// OnSkillPP() bool //技能PP减少节点
// BeforeMultiHit() bool //多段攻击前
// BeforeHit() bool //命中前
// OnCritPreDamage() bool //暴击判定成功且伤害计算前触发
// PreDamage() bool // 技能伤害计算前触发(增伤 / 减伤等)
// OnBeforeCalculateDamage() bool // 最终伤害计算前触发
// OnDamage() bool // 造成伤害时触发
//使用技能 可以取消用技能节点
SetArgs(param []int)
IsCrit(attacker, defender *Input, skill *info.BattleSkillEntity) bool //是否暴击
CalculateDamage(attacker, defender *Input, skill *info.BattleSkillEntity) //击判定成功且伤害计算前触发
// Shield() bool // 护盾值变化时触发
// PostDamage() bool // 伤害结算后触发(血量扣除后)
IsHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
TakeHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
//() bool // 暴击伤害结算后触发
// OnHit() bool // 技能命中时触发
// OnMiss() bool // 技能未命中时触发
// AfterAttacked() bool // 被攻击后触发(受击判定)
// SetOwner(bool)
// OnDefeat() bool // 精灵被击败时触发
// TurnEnd() bool // 回合结束
// // 堆叠Stack相关触发
// OnStackBefore() bool // 堆叠效果前触发
// OnStack() bool // 堆叠效果触发
// OnBeforeConsumeStack() bool // 消耗堆叠前触发
// OnConsumeStack() bool // 消耗堆叠时触发
// // 治疗相关触发
// OnBeforeHeal() bool // 治疗前触发
// OnHeal() bool // 治疗生效时触发
// // // 怒气Rage相关触发
// // BeforeRageGain EnumEffectTrigger enum:"37" // 增怒前触发
// // OnRageGain EnumEffectTrigger enum:"38" // 增怒时触发
// // BeforeRageLoss EnumEffectTrigger enum:"39" // 减怒前触发
// // OnRageLoss EnumEffectTrigger enum:"40" // 减怒时触发
// // 精灵切换相关触发
// OnSwitchIn() bool // 精灵出战 / 上场时触发
// OnSwitchOut() bool // 精灵下场时触发
// OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
// OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
// PreBattleEnd() bool //战斗结束前
// OnBattleEnd() bool //战斗结束
Duration(int) int
ID() int
GetArgSize() int
Stack(int) int
MaxStack() int
GetOwner() bool // 技能属主,比如寄生和镇魂歌,属主是对方)
//GetSkill() *BattleSkillEntity //获得技能ctx
}
// ========================
// 容器:存放多个效果
// ========================
type NodeManager struct {
//GlobalEffects []*Effect // 全局常驻/回合/次数效果
Effects []Effect //effects 实际上全局就是effect无限回合
}
var NodeM = make(map[int]Effect, 0)
func InitEffect(id int, t Effect) {
NodeM[id] = t
}
func getTypeName(v interface{}) string {
// 获取类型信息
t := reflect.TypeOf(v)
// 如果是指针类型,需要先获取其指向的元素类型
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 如果是结构体类型,返回其名称
if t.Kind() == reflect.Struct {
return t.Name()
}
// 非结构体类型返回空或对应的类型名
return t.Kind().String()
}
func (c *NodeManager) AddEffect(e Effect) {
// 如果已有同 ID 的效果,尝试叠加
for _, eff := range c.Effects {
if eff.ID() == e.ID() {
if eff.Stack(0) < eff.MaxStack() { //如果小于最大叠层
eff.Stack(eff.Stack(0)) //获取到当前叠层数然后叠加
} else {
//这里,说明是延续回合效果
eff.Duration(eff.Duration(0))
}
return
}
}
// 否则新加入
c.Effects = append(c.Effects, e)
}
// 删除
func (c *NodeManager) RemoveEffect(e Effect) {
var remain []Effect
for _, eff := range c.Effects {
if eff.ID() != e.ID() {
remain = append(remain, eff)
}
}
c.Effects = remain
}
// ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// 返回值:所有 Effect 的方法返回值列表
func (c *NodeManager) Exec(fn func(Effect) bool) bool {
var results bool
for _, effect := range c.Effects {
result := fn(effect)
if !result {
results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} else {
break //如果是false,停止向下循环
}
}
return results
}
// 消除回合类效果 efftype 输入是消对方的还是自己的,false是自己,true是对方
func (c *NodeManager) CancelTurn(efftype bool) {
var remain []Effect
for _, eff := range c.Effects {
if eff.Duration(0) <= 0 && eff.GetOwner() == efftype { //false是自身,true是对方,反转后为真就是自己的
remain = append(remain, eff)
}
}
c.Effects = remain
}
// // 添加效果
// func (c **NodeManager) AddEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.AddEffect(e)
// }
// // 删除
// func (c **NodeManager) RemoveEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.RemoveEffect(e)
// }
// // ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// // 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// // 返回值:所有 Effect 的方法返回值列表
// func (c *NodeManagerE) Exec(fn func(Effect) bool) bool {
// // var results bool
// // execfun := func(nm *NodeManager) {
// // for _, effect := range nm.Effects {
// // result := fn(effect)
// // if !result {
// // results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
// // }
// // }
// // }
// results := true
// result := c.Mark.Exec(fn) //执行叠层
// if !result {
// results = result
// }
// result = c.Turn.Exec(fn) //执行回合类效果
// if !result {
// results = result
// }
// return results
// }

View File

@@ -0,0 +1,218 @@
package input
import (
"blazing/logic/service/fight/info"
"reflect"
)
type Effect interface {
OnBattleStart() bool //战斗开始
OnTurnStart(attacker, defender *Input) bool //回合开始
UseSkill(attacker, defender *Input) bool //使用技能 可以取消用技能节点
// OnSkillPP() bool //技能PP减少节点
// BeforeMultiHit() bool //多段攻击前
// BeforeHit() bool //命中前
// OnCritPreDamage() bool //暴击判定成功且伤害计算前触发
// PreDamage() bool // 技能伤害计算前触发(增伤 / 减伤等)
// OnBeforeCalculateDamage() bool // 最终伤害计算前触发
// OnDamage() bool // 造成伤害时触发
//使用技能 可以取消用技能节点
SetArgs(param []int)
IsCrit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //是否暴击
CalculateDamage(attacker, defender *Input, skill *info.BattleSkillEntity) //击判定成功且伤害计算前触发
// Shield() bool // 护盾值变化时触发
// PostDamage() bool // 伤害结算后触发(血量扣除后)
IsHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
TakeHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
//() bool // 暴击伤害结算后触发
// OnHit() bool // 技能命中时触发
// OnMiss() bool // 技能未命中时触发
// AfterAttacked() bool // 被攻击后触发(受击判定)
// SetOwner(bool)
// OnDefeat() bool // 精灵被击败时触发
// TurnEnd() bool // 回合结束
// // 堆叠Stack相关触发
// OnStackBefore() bool // 堆叠效果前触发
// OnStack() bool // 堆叠效果触发
// OnBeforeConsumeStack() bool // 消耗堆叠前触发
// OnConsumeStack() bool // 消耗堆叠时触发
// // 治疗相关触发
// OnBeforeHeal() bool // 治疗前触发
// OnHeal() bool // 治疗生效时触发
// // // 怒气Rage相关触发
// // BeforeRageGain EnumEffectTrigger enum:"37" // 增怒前触发
// // OnRageGain EnumEffectTrigger enum:"38" // 增怒时触发
// // BeforeRageLoss EnumEffectTrigger enum:"39" // 减怒前触发
// // OnRageLoss EnumEffectTrigger enum:"40" // 减怒时触发
// // 精灵切换相关触发
// OnSwitchIn() bool // 精灵出战 / 上场时触发
// OnSwitchOut() bool // 精灵下场时触发
// OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
// OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
// PreBattleEnd() bool //战斗结束前
// OnBattleEnd() bool //战斗结束
Duration(int) int
ID() int
GetArgSize() int
Stack(int) int
MaxStack() int
GetOwner() bool // 技能属主,比如寄生和镇魂歌,属主是对方)
//GetSkill() *BattleSkillEntity //获得技能ctx
}
// ========================
// 容器:存放多个效果
// ========================
type NodeManager struct {
//GlobalEffects []*Effect // 全局常驻/回合/次数效果
Effects []Effect //effects 实际上全局就是effect无限回合
}
var NodeM = make(map[int]Effect, 0)
func InitEffect(id int, t Effect) {
NodeM[id] = t
}
func getTypeName(v interface{}) string {
// 获取类型信息
t := reflect.TypeOf(v)
// 如果是指针类型,需要先获取其指向的元素类型
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// 如果是结构体类型,返回其名称
if t.Kind() == reflect.Struct {
return t.Name()
}
// 非结构体类型返回空或对应的类型名
return t.Kind().String()
}
func (c *NodeManager) AddEffect(e Effect) {
// 如果已有同 ID 的效果,尝试叠加
for _, eff := range c.Effects {
if eff.ID() == e.ID() {
if eff.Stack(0) < eff.MaxStack() { //如果小于最大叠层
eff.Stack(eff.Stack(0)) //获取到当前叠层数然后叠加
} else {
//这里,说明是延续回合效果
eff.Duration(eff.Duration(0))
}
return
}
}
// 否则新加入
c.Effects = append(c.Effects, e)
}
// 删除
func (c *NodeManager) RemoveEffect(e Effect) {
var remain []Effect
for _, eff := range c.Effects {
if eff.ID() != e.ID() {
remain = append(remain, eff)
}
}
c.Effects = remain
}
// ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// 返回值:所有 Effect 的方法返回值列表
func (c *NodeManager) Exec(fn func(Effect) bool) bool {
var results bool
for _, effect := range c.Effects {
result := fn(effect)
if !result {
results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} else {
break //如果是false,停止向下循环
}
}
return results
}
// 消除回合类效果 efftype 输入是消对方的还是自己的,false是自己,true是对方
func (c *NodeManager) CancelTurn(efftype bool) {
var remain []Effect
for _, eff := range c.Effects {
if eff.Duration(0) <= 0 && eff.GetOwner() == efftype { //false是自身,true是对方,反转后为真就是自己的
remain = append(remain, eff)
}
}
c.Effects = remain
}
// // 添加效果
// func (c **NodeManager) AddEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.AddEffect(e)
// }
// // 删除
// func (c **NodeManager) RemoveEffect(e Effect) {
// var fff *NodeManager
// switch t := (e.Duration(0) > 1); t { //判断是否是回合类效果
// case t: //t>0就是回合类效果了 -1和0都是非回合效果和无限效果,无法被断回合
// fff = c.Turn
// default:
// fff = c.Mark
// }
// fff.RemoveEffect(e)
// }
// // ForEachEffectBool 遍历所有 Effect执行“无参数、返回 bool”的方法
// // 参数 fn接收单个 Effect返回 bool如 func(e Effect) bool { return e.OnBattleStart() }
// // 返回值:所有 Effect 的方法返回值列表
// func (c *NodeManagerE) Exec(fn func(Effect) bool) bool {
// // var results bool
// // execfun := func(nm *NodeManager) {
// // for _, effect := range nm.Effects {
// // result := fn(effect)
// // if !result {
// // results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
// // }
// // }
// // }
// results := true
// result := c.Mark.Exec(fn) //执行叠层
// if !result {
// results = result
// }
// result = c.Turn.Exec(fn) //执行回合类效果
// if !result {
// results = result
// }
// return results
// }

View File

@@ -1,6 +1,9 @@
package common
import "blazing/logic/service/fight/info"
import (
"blazing/logic/service/fight/info"
"math/rand"
)
type FightI interface {
Escape(c PlayerI) //逃跑
@@ -10,4 +13,5 @@ type FightI interface {
ReadyFight(c PlayerI) //是否准备战斗
ChangePet(c PlayerI, id uint32)
Capture(c PlayerI, id uint32)
GetRand() *rand.Rand
}

View File

@@ -91,6 +91,12 @@ type BaseAction struct {
}
func NewBaseAction(t uint32) BaseAction {
return BaseAction{
PlayerID: t,
}
}
func (a *BaseAction) GetPlayerID() uint32 {
return a.PlayerID
// fmt.Printf("玩家[%d]主动切换宠物:从%s切换到%s原因%s\n",

View File

@@ -0,0 +1,122 @@
package fight
import (
"blazing/common/data/xmlres"
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"blazing/logic/service/player"
"math"
"github.com/gogf/gf/v2/util/gconv"
"github.com/jinzhu/copier"
"github.com/panjf2000/ants/v2"
)
// 玩家逃跑
func (f *FightC) Escape(c common.PlayerI) {
ret := &EscapeAction{
BaseAction: NewBaseAction(c.GetInfo().UserID),
Reason: info.FightOverInfo{
Reason: uint32(info.BattleOverReason.PlayerEscape),
},
}
f.actionChan <- ret
}
// 玩家掉线
func (f *FightC) Offline(c common.PlayerI) {
ret := &PlayerOfflineAction{
BaseAction: NewBaseAction(c.GetInfo().UserID),
Reason: info.FightOverInfo{
Reason: uint32(info.BattleOverReason.PlayerOffline),
},
}
f.actionChan <- ret
}
// 切换精灵 主动和被驱逐
func (f *FightC) ChangePet(c common.PlayerI, id uint32) {
ret := &ActiveSwitchAction{
BaseAction: NewBaseAction(c.GetInfo().UserID),
}
f.GetInputByPlayer(c, false).CurrentPet, ret.Reason = f.GetInputByPlayer(c, false).GetPet(id)
f.actionChan <- ret
}
// 玩家使用技能
func (f *FightC) UseSkill(c common.PlayerI, id int32) {
if id == 0 {
f.actionChan <- &SystemGiveUpAction{BaseAction: NewBaseAction(c.GetInfo().UserID)}
return
}
ret := &SelectSkillAction{
PlayerID: c.ID(),
}
ret.PetInfo = f.GetInputByPlayer(c, false).CurrentPet
for _, v := range ret.PetInfo.Skills {
if v != nil && v.ID == int(id) {
ret.Skill = v
}
}
f.actionChan <- ret
}
// 玩家使用技能
func (f *FightC) Capture(c common.PlayerI, id uint32) {
f.actionChan <- &UseItemAction{BaseAction: NewBaseAction(c.GetInfo().UserID), ItemID: id}
}
// 战斗准备
func (f *FightC) ReadyFight(c common.PlayerI) {
rett := info.FightStartOutboundInfo{}
copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) // 复制自己的信息
copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0])
rett.Info1.UserID = f.Info.OurInfo.UserID //
rett.Info2.UserID = f.Info.OpponentInfo.UserID
rrsult := func() { //传回函数
f.Our.Player.SendReadyToFightInfo(rett)
f.Opp.Player.SendReadyToFightInfo(rett)
}
switch f.Info.FightId {
case 1: // 1v1
f.GetInputByPlayer(c, false).Finished = true
if f.GetInputByPlayer(c, true).Finished {
rrsult()
}
case 3: // 野怪战斗
//判断捕捉率大于0
if gconv.Int(xmlres.PetMAP[int(f.Info.OpponentPetList[0].ID)].CatchRate) > 0 {
rett.Info2.Catchable = 1
t, _ := f.Opp.Player.(*player.AI_player)
t.CanCapture = true
}
rrsult()
}
}
var Fightpool *ants.Pool
func init() {
Fightpool, _ = ants.NewPool(math.MaxInt32)
//defer p.Release()
}

View File

@@ -1,6 +1,7 @@
package effect
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
@@ -12,7 +13,7 @@ type Effect1 struct {
}
func init() {
node.InitEffect(1, &Effect1{})
input.InitEffect(1, &Effect1{})
}

View File

@@ -1,6 +1,7 @@
package effect
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
@@ -13,7 +14,7 @@ type Effect62 struct {
}
func init() {
node.InitEffect(62, &Effect62{
input.InitEffect(62, &Effect62{
EffectNode: node.EffectNode{
ArgSize: 1,
},

View File

@@ -1,6 +1,7 @@
package effect
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
@@ -12,7 +13,7 @@ type Effect67 struct {
}
func init() {
node.InitEffect(67, &Effect67{
input.InitEffect(67, &Effect67{
EffectNode: node.EffectNode{
ArgSize: 1,
},

View File

@@ -1,6 +1,7 @@
package effect
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
@@ -9,7 +10,7 @@ import (
*/
func init() {
node.InitEffect(9, &Effect9{
input.InitEffect(9, &Effect9{
EffectNode: node.EffectNode{
ArgSize: 2,
},

View File

@@ -1,18 +1,19 @@
package effect
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
)
func init() {
//技能使用成功时m%自身XX等级+/-n
node.InitEffect(4, NewEffectStat(false))
input.InitEffect(4, NewEffectStat(false))
//技能使用成功时m%对方XX等级+/-n
node.InitEffect(5, NewEffectStat(true))
input.InitEffect(5, NewEffectStat(true))
}
func NewEffectStat(b bool) node.Effect {
func NewEffectStat(b bool) input.Effect {
return &EffectStat{
node.EffectNode{

View File

@@ -1,23 +1,17 @@
package fight
import (
"blazing/common/data/xmlres"
"blazing/common/utils"
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
"blazing/logic/service/player"
"fmt"
"math"
"math/rand"
"sort"
"time"
"github.com/gogf/gf/v2/util/gconv"
"github.com/jinzhu/copier"
"github.com/mohae/deepcopy"
"github.com/panjf2000/ants/v2"
"github.com/shopspring/decimal"
)
@@ -31,9 +25,9 @@ type FightC struct {
StartTime time.Time
actionChan chan BattleActionI // 所有操作统一从这里进入
Round int //回合数
EffectS node.NodeManager //effects容器
First *input.Input
Second *input.Input
First *input.Input
Second *input.Input
}
func (f *FightC) Ownerid() uint32 {
@@ -62,71 +56,6 @@ func (f *FightC) GetInputByAction(c BattleActionI, isOpposite bool) *input.Input
return f.Opp
}
// 玩家逃跑
func (f *FightC) Escape(c common.PlayerI) {
ret := &EscapeAction{
PlayerID: c.ID(),
Reason: info.FightOverInfo{
Reason: uint32(info.BattleOverReason.PlayerEscape),
},
}
f.actionChan <- ret
}
// 玩家掉线
func (f *FightC) Offline(c common.PlayerI) {
ret := &PlayerOfflineAction{
PlayerID: c.ID(),
Reason: info.FightOverInfo{
Reason: uint32(info.BattleOverReason.PlayerOffline),
},
}
f.actionChan <- ret
}
// 切换精灵 主动和被驱逐
func (f *FightC) ChangePet(c common.PlayerI, id uint32) {
ret := &ActiveSwitchAction{
PlayerID: c.ID(),
}
f.GetInputByPlayer(c, false).CurrentPet, ret.Reason = f.GetInputByPlayer(c, false).GetPet(id)
f.actionChan <- ret
}
// 玩家使用技能
func (f *FightC) UseSkill(c common.PlayerI, id int32) {
if id == 0 {
f.actionChan <- &SystemGiveUpAction{PlayerID: c.ID()}
return
}
ret := &SelectSkillAction{
PlayerID: c.ID(),
}
ret.PetInfo = f.GetInputByPlayer(c, false).CurrentPet
for _, v := range ret.PetInfo.Skills {
if v != nil && v.ID == int(id) {
ret.Skill = v
}
}
f.actionChan <- ret
}
// 玩家使用技能
func (f *FightC) Capture(c common.PlayerI, id uint32) {
f.actionChan <- &UseItemAction{PlayerID: c.ID(), ItemID: id}
}
// 玩家使用技能
func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
if f.Our.Player.ID() == c.ID() {
@@ -137,49 +66,13 @@ func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
}
// 战斗准备
func (f *FightC) ReadyFight(c common.PlayerI) {
rett := info.FightStartOutboundInfo{}
// 获取随机数
func (f *FightC) GetRand() *rand.Rand {
copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) // 复制自己的信息
copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0])
rett.Info1.UserID = f.Info.OurInfo.UserID //
rett.Info2.UserID = f.Info.OpponentInfo.UserID
return f.rand
rrsult := func() { //传回函数
f.Our.Player.SendReadyToFightInfo(rett)
f.Opp.Player.SendReadyToFightInfo(rett)
}
switch f.Info.FightId {
case 1: // 1v1
f.GetInputByPlayer(c, false).Finished = true
if f.GetInputByPlayer(c, true).Finished {
rrsult()
}
case 3: // 野怪战斗
//判断捕捉率大于0
if gconv.Int(xmlres.PetMAP[int(f.Info.OpponentPetList[0].ID)].CatchRate) > 0 {
rett.Info2.Catchable = 1
t, _ := f.Opp.Player.(*player.AI_player)
t.CanCapture = true
}
rrsult()
}
}
var Fightpool *ants.Pool
func init() {
Fightpool, _ = ants.NewPool(math.MaxInt32)
//defer p.Release()
}
func (f *FightC) initplayer(c common.PlayerI, opp bool) {
temp := &input.Input{Player: c}
@@ -192,15 +85,7 @@ func (f *FightC) initplayer(c common.PlayerI, opp bool) {
}
}
// for k, v := range c.GetPetInfo() {
// if f.Info.MAXPET == 0 || k < int(f.Info.MAXPET) {
// temppet := v //这里必须先复制,否则会导致字段混乱
// temp.AllPet = append(temp.AllPet, info.CreateBattlePetEntity(&temppet, f.rand))
// }
// }
// // 排序:血量>0的放前面血量=0的放后面同类型保持原有顺序
sort.Slice(temp.AllPet, func(i, j int) bool {
x, y := temp.AllPet[i], temp.AllPet[j]
// 若x血量>0且y血量=0则x排在前
@@ -380,10 +265,12 @@ func (f *FightC) battleLoop() {
fmt.Println("回合操作超时")
if _, exists := actions[f.Our.Player.ID()]; !exists {
actions[f.Our.Player.ID()] = &SystemGiveUpAction{PlayerID: f.Our.Player.ID()} //系统选择出手
actions[f.Our.Player.ID()] = &SystemGiveUpAction{
BaseAction: NewBaseAction(f.Our.Player.ID())} //系统选择出手
}
if _, exists := actions[f.Opp.Player.ID()]; !exists {
actions[f.Opp.Player.ID()] = &SystemGiveUpAction{PlayerID: f.Opp.Player.ID()} //系统选择出手
actions[f.Opp.Player.ID()] = &SystemGiveUpAction{BaseAction: NewBaseAction(f.Opp.Player.ID())} //系统选择出手
}
}
}
@@ -393,6 +280,7 @@ func (f *FightC) battleLoop() {
p1Action := actions[f.Our.Player.ID()]
p2Action := actions[f.Opp.Player.ID()]
fmt.Println("开始结算回合")
var BattleActionI [2]BattleActionI
BattleActionI[0], BattleActionI[1] = Compare(p1Action, p2Action)
@@ -410,11 +298,11 @@ func (f *FightC) battleLoop() {
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
})
break
case *ActiveSwitchAction: //切换上场的
f.enterturn(BattleActionI[1], nil) //切换,相当于后手直接出手
case *ActiveSwitchAction: //切换上场的,切换方放弃出手
f.enterturn(BattleActionI[1], &SystemGiveUpAction{BaseAction: NewBaseAction(BattleActionI[0].GetPlayerID())}) //切换,相当于后手直接出手
case *UseItemAction: //使用道具
fmt.Println(faction.ItemID)
//fmt.Println(faction.ItemID)
switch {
case faction.ItemID >= 30001 && faction.ItemID <= 300010: //胶囊
@@ -452,7 +340,7 @@ func (f *FightC) battleLoop() {
// 其他情况
fmt.Println("ItemID 不在指定范围内")
}
f.enterturn(BattleActionI[1], &SystemGiveUpAction{BaseAction: NewBaseAction(BattleActionI[0].GetPlayerID())}) //切换,相当于后手直接出手
case *SelectSkillAction: //选择技能
//回合前操作,比如挂载buff
@@ -466,23 +354,28 @@ func (f *FightC) battleLoop() {
}
// 解析并 施加effect
func (f *FightC) parseskill(id *SelectSkillAction) {
func (f *FightC) parseskill(attacker, defender *input.Input, id *SelectSkillAction) {
temparg := id.Skill.SideEffectArgS
for _, v := range id.Skill.SideEffectS {
t, ok := node.NodeM[v]
t, ok := input.NodeM[v]
if ok { //获取成功
args := t.GetArgSize()
t.SetArgs(temparg[:args]) //设置入参
//如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
//所以这里要对target取反
if id.GetPlayerID() != f.ownerID {
t.SetOwner(!t.GetOwner())
}
// //如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
// //所以这里要对target取反
// if id.GetPlayerID() != f.ownerID {
// t.SetOwner(!t.GetOwner())
// }
temparg = temparg[args:]
f.EffectS.AddEffect(deepcopy.Copy(t).(node.Effect))
if t.GetOwner() { //如果取反,说明是给对方添加的回合效果
//实际上,owner永远为反,说明是对方给我添加的
defender.Effect.AddEffect(deepcopy.Copy(t).(input.Effect))
} else {
attacker.Effect.AddEffect(deepcopy.Copy(t).(input.Effect))
}
}
@@ -514,55 +407,32 @@ func (f *FightC) initAttackers(fattack, sattack BattleActionI) {
// 处理技能攻击逻辑
func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *SelectSkillAction) {
f.parseskill(skill) //解析effect
// 记录技能信息
attacker.AttackValue.SkillID = uint32(skill.Skill.ID) //获取技能ID
attacker.AttackValue.AttackTime = skill.Skill.AttackTime() //计算命中
attacker.AttackValue.SkillID = uint32(skill.Skill.ID) //获取技能ID
f.EffectS.Exec(func(t node.Effect) bool { //计算闪避 闪避就是命中率重新计算的结果
//闪避本质上是算对方的命中重写函数?
attacker.Effect.Exec(func(t input.Effect) bool { //计算命中
if attacker.UserID == f.Our.Player.ID() {
}
if !t.GetOwner() { //先获取对方的
return t.AttackTime(attacker, defender)
}
return true
attacker.AttackValue.AttackTime = t.IsHit(attacker, defender, skill.Skill)
return attacker.AttackTime == 0 //等于0,继续处理
})
defender.Effect.Exec(func(t input.Effect) bool { //计算命中
if attacker.AttackValue.AttackTime == 1 { //如果命中
attacker.AttackValue.AttackTime = t.TakeHit(attacker, defender, skill.Skill)
return attacker.AttackTime > 0 //
})
if attacker.AttackValue.AttackTime > 0 { //如果命中
spower := skill.Skill.CalculatePower(defender.CurrentPet)
attacker.Damage = spower
CritRate := utils.Max(skill.Skill.CritRate, 1)
CritRateR := f.rand.Int31n(16)
//CritAtkFirst: 先出手时必定致命一击; 默认: 0
if skill.Skill.CritAtkFirst != 0 && attacker == f.First {
CritRate = 16
}
//CritAtkSecond: 后出手时必定致命一击; 默认: 0
if skill.Skill.CritAtkSecond != 0 && attacker != f.First {
CritRate = 16
}
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
if skill.Skill.CritSelfHalfHp != 0 && (attacker.CurrentPet.HP < int(attacker.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
if skill.Skill.CritSelfHalfHp != 0 && (defender.CurrentPet.HP < int(defender.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
//todo 暴击伤害
if CritRateR <= int32(CritRate) {
attacker.AttackValue.IsCritical = 1
}
attacker.Effect.Exec(func(t input.Effect) bool { //计算暴击率加成
attacker.AttackValue.IsCritical = t.IsCrit(attacker, defender, skill.Skill)
return attacker.AttackValue.IsCritical == 0
})
if attacker.AttackValue.IsCritical == 1 {
attacker.Damage.Mul(decimal.NewFromInt(2)) //暴击翻倍
}
if uint32(attacker.Damage.IntPart()) > defender.CurrentPet.Info.Hp {
defender.CurrentPet.Info.Hp = 0
} else {
@@ -575,23 +445,25 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *Sele
}
func (f *FightC) enterturn(fattack, sattack BattleActionI) {
f.initAttackers(fattack, sattack) //初始化先后手
var attacker, defender *input.Input
//**回合开始前enterturn
attacker.Effect.Exec(func(t input.Effect) bool { //计算命中
//结算状态
t.OnTurnStart(attacker, defender)
return true
})
//开始回合操作
for i := 0; i < 2; i++ {
var attackeraction BattleActionI
if i == 0 { //
attacker, defender = f.First, f.Second
attackeraction = fattack
//attacker.BattleActionI, defender.BattleActionI = fattack, sattack
} else {
attacker, defender = f.Second, f.First
attackeraction = sattack
}
skill, ok := attackeraction.(*SelectSkillAction)
if !ok { //还有系统选择放弃出手的
f.parseskill(attacker, defender, skill) //解析effect
if !ok { //还有系统选择放弃出手的
continue
}
@@ -599,13 +471,35 @@ func (f *FightC) enterturn(fattack, sattack BattleActionI) {
continue
}
f.processSkillAttack(attacker, defender, skill)
if i == 0 { //
attacker, defender = f.First, f.Second
attackeraction = fattack
skill.Skill.First = true //先手技能
//attacker.BattleActionI, defender.BattleActionI = fattack, sattack
} else {
attacker, defender = f.Second, f.First
attackeraction = sattack
skill.Skill.First = false //后手技能
}
canuseskill := attacker.Effect.Exec(func(t input.Effect) bool { //计算命中
//结算状态
return t.UseSkill(attacker, defender) //返回本身结算,如果false,说明不能使用技能了
})
if canuseskill { //可以使用技能
f.processSkillAttack(attacker, defender, skill)
skill.Skill.Info.PP-- //减少PP
}
fmt.Println(i,
"玩家技能伤害:", attacker.Damage,
"自身剩余血量:", attacker.CurrentPet.Info.Hp,
"对手剩余血量:", defender.CurrentPet.Info.Hp,
)
skill.Skill.Info.PP-- //减少PP
if defender.CurrentPet.Info.Hp == 0 {
defender.CanChange = true //被打死就可以切精灵了
if f.IsWin(attacker, defender.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束

View File

@@ -43,6 +43,7 @@ type BattleSkillEntity struct {
Rand *rand.Rand
Pet *BattlePetEntity
First bool
Hit uint32
}
// CreateBattleSkillWithInfinity 创建战斗技能实例可指定是否无限PP
@@ -164,14 +165,15 @@ var DamageC = enum.New[struct {
// 计算是否命中
func (s *BattleSkillEntity) AttackTime() uint32 {
s.Hit = 0 //先重置上一次的
if s.MustHit != 0 {
return 1
s.Hit = 2
}
if int64(s.Pet.Accuracy(int64(s.Accuracy))) > s.Rand.Int63n(100) {
return 1
s.Hit = 1
}
return 0
return s.Hit
}
func (s *BattleSkillEntity) CriticalsameTypeBonus() decimal.Decimal {

View File

@@ -74,7 +74,7 @@ func NewAttackValue(userid uint32) *AttackValue {
type AttackValue struct {
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
SkillID uint32 `json:"skillId" fieldDescription:"使用技能的id"`
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中"`
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中,2为必中"`
LostHp uint32 `json:"lostHp" fieldDescription:"我方造成的伤害"`
GainHp int32 `json:"gainHp" fieldDescription:"我方获得血量"`
RemainHp int32 `json:"remainHp" fieldDescription:"我方剩余血量"`

View File

@@ -17,7 +17,9 @@ type Input struct {
*info.AttackValue
FightC common.FightI
// info.BattleActionI
Effect NodeManager //effects容器OurEffect node.NodeManager //effects容器
Damage decimal.Decimal //造成伤害
First bool
}
func (i *Input) GetPetInfo() *info.BattlePetEntity {

View File

@@ -1,69 +1,73 @@
package node
package input
import (
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/info"
"reflect"
)
type Effect interface {
OnBattleStart() bool //战斗开始
OnTurnStart() bool //回合开始
OnTurnStart(attacker, defender *Input) bool //回合开始
UseSkill() bool //使用技能 可以取消用技能节点
OnSkillPP() bool //技能PP减少节点
BeforeMultiHit() bool //多段攻击前
BeforeHit() bool //命中前
OnCritPreDamage() bool //暴击判定成功且伤害计算前触发
PreDamage() bool // 技能伤害计算前触发(增伤 / 减伤等)
OnBeforeCalculateDamage() bool // 最终伤害计算前触发
OnDamage() bool // 造成伤害时触发
SetArgs(param []int) //设置参数
UseSkill(attacker, defender *Input) bool //使用技能 可以取消用技能节点
// OnSkillPP() bool //技能PP减少节点
// BeforeMultiHit() bool //多段攻击前
// BeforeHit() bool //命中前
// OnCritPreDamage() bool //暴击判定成功且伤害计算前触发
// PreDamage() bool // 技能伤害计算前触发(增伤 / 减伤等)
// OnBeforeCalculateDamage() bool // 最终伤害计算前触发
// OnDamage() bool // 造成伤害时触发
//使用技能 可以取消用技能节点
SetArgs(param []int)
IsCrit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //是否暴击
CalculateDamage(attacker, defender *Input, skill *info.BattleSkillEntity) //击判定成功且伤害计算前触发
// Shield() bool // 护盾值变化时触发
// PostDamage() bool // 伤害结算后触发(血量扣除后)
IsHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
TakeHit(attacker, defender *Input, skill *info.BattleSkillEntity) uint32 //闪避率计算,,实际上是修改命中的判断
//() bool // 暴击伤害结算后触发
Shield() bool // 护盾值变化时触发
PostDamage() bool // 伤害结算后触发(血量扣除后)
AttackTime(*input.Input, *input.Input) bool //闪避率计算,,实际上是修改命中的判断
OnCritPostDamage() bool // 暴击伤害结算后触发
// OnHit() bool // 技能命中时触发
// OnMiss() bool // 技能未命中时触发
// AfterAttacked() bool // 被攻击后触发(受击判定)
OnHit() bool // 技能命中时触发
OnMiss() bool // 技能未命中时触发
AfterAttacked() bool // 被攻击后触发(受击判定)
GetOwner() bool // 技能属主,比如寄生和镇魂歌,属主是对方)
SetOwner(bool)
OnDefeat() bool // 精灵被击败时触发
// SetOwner(bool)
// OnDefeat() bool // 精灵被击败时触发
TurnEnd() bool // 回合结束
// TurnEnd() bool // 回合结束
// 堆叠Stack相关触发
OnStackBefore() bool // 堆叠效果前触发
OnStack() bool // 堆叠效果触发
OnBeforeConsumeStack() bool // 消耗堆叠前触发
OnConsumeStack() bool // 消耗堆叠时触发
// // 堆叠Stack相关触发
// OnStackBefore() bool // 堆叠效果前触发
// OnStack() bool // 堆叠效果触发
// OnBeforeConsumeStack() bool // 消耗堆叠前触发
// OnConsumeStack() bool // 消耗堆叠时触发
// 治疗相关触发
OnBeforeHeal() bool // 治疗前触发
OnHeal() bool // 治疗生效时触发
// // 治疗相关触发
// OnBeforeHeal() bool // 治疗前触发
// OnHeal() bool // 治疗生效时触发
// // 怒气Rage相关触发
// BeforeRageGain EnumEffectTrigger enum:"37" // 增怒前触发
// OnRageGain EnumEffectTrigger enum:"38" // 增怒时触发
// BeforeRageLoss EnumEffectTrigger enum:"39" // 减怒前触发
// OnRageLoss EnumEffectTrigger enum:"40" // 减怒时触发
// // // 怒气Rage相关触发
// // BeforeRageGain EnumEffectTrigger enum:"37" // 增怒前触发
// // OnRageGain EnumEffectTrigger enum:"38" // 增怒时触发
// // BeforeRageLoss EnumEffectTrigger enum:"39" // 减怒前触发
// // OnRageLoss EnumEffectTrigger enum:"40" // 减怒时触发
// 精灵切换相关触发
OnSwitchIn() bool // 精灵出战 / 上场时触发
OnSwitchOut() bool // 精灵下场时触发
OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
// // 精灵切换相关触发
// OnSwitchIn() bool // 精灵出战 / 上场时触发
// OnSwitchOut() bool // 精灵下场时触发
// OnOwnerSwitchIn() bool // 所属玩家精灵出战时触发
// OnOwnerSwitchOut() bool // 所属玩家精灵下场时触发
PreBattleEnd() bool //战斗结束前
OnBattleEnd() bool //战斗结束
// PreBattleEnd() bool //战斗结束前
// OnBattleEnd() bool //战斗结束
Duration(int) int
ID() int
GetArgSize() int
Stack(int) int
MaxStack() int
GetOwner() bool // 技能属主,比如寄生和镇魂歌,属主是对方)
//GetSkill() *BattleSkillEntity //获得技能ctx
}
@@ -140,6 +144,8 @@ func (c *NodeManager) Exec(fn func(Effect) bool) bool {
result := fn(effect)
if !result {
results = result //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} else {
break //如果是false,停止向下循环
}
}
return results

View File

@@ -1,5 +1,7 @@
package node
import "blazing/logic/service/fight/input"
// 回合开始前
func (this *EffectNode) PreTurnStart() bool {
return true
@@ -7,7 +9,7 @@ func (this *EffectNode) PreTurnStart() bool {
}
// 回合开始
func (this *EffectNode) OnTurnStart() bool {
func (this *EffectNode) OnTurnStart(attacker, defender *input.Input) bool {
return true
}

View File

@@ -1,6 +1,23 @@
package node
func (this *EffectNode) UseSkill() bool {
import (
"blazing/common/utils"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
)
// 技能命中计算
func (this *EffectNode) IsHit(attacker, defender *input.Input, skill *info.BattleSkillEntity) uint32 {
return skill.AttackTime() //计算命中
}
// 被命中计算,默认直接返回,重写这个来实现闪避率
func (this *EffectNode) TakeHit(attacker, defender *input.Input, skill *info.BattleSkillEntity) uint32 {
return skill.Hit
}
func (this *EffectNode) UseSkill(attacker, defender *input.Input) bool {
panic("not implemented") // TODO: Implement
}
func (this *EffectNode) OnSkillPP() bool {
@@ -26,7 +43,7 @@ func (this *EffectNode) PreDamage() bool {
panic("not implemented") // TODO: Implement
}
func (this *EffectNode) OnBeforeCalculateDamage() bool {
func (this *EffectNode) CalculateDamage(attacker, defender *input.Input, skill *info.BattleSkillEntity) {
panic("not implemented") // TODO: Implement
}
@@ -42,8 +59,32 @@ func (this *EffectNode) PostDamage() bool {
panic("not implemented") // TODO: Implement
}
func (this *EffectNode) OnCritPostDamage() bool {
panic("not implemented") // TODO: Implement
func (this *EffectNode) IsCrit(attacker, defender *input.Input, skill *info.BattleSkillEntity) uint32 {
CritRate := utils.Max(skill.CritRate, 1)
CritRateR := attacker.FightC.GetRand().Int31n(16)
//CritAtkFirst: 先出手时必定致命一击; 默认: 0
if skill.CritAtkFirst != 0 && skill.First {
CritRate = 16
}
//CritAtkSecond: 后出手时必定致命一击; 默认: 0
if skill.CritAtkSecond != 0 && !skill.First {
CritRate = 16
}
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
if skill.CritSelfHalfHp != 0 && (attacker.CurrentPet.HP < int(attacker.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
if skill.CritSelfHalfHp != 0 && (defender.CurrentPet.HP < int(defender.CurrentPet.Info.MaxHp)/2) {
CritRate = 16
}
//todo 暴击伤害
if CritRateR <= int32(CritRate) {
return 1
}
return 0
}
func (this *EffectNode) OnHit() bool {