perf: 优化战斗逻辑性能与内存分配
This commit is contained in:
@@ -44,6 +44,7 @@ func (f *FightC) openActionWindow() {
|
|||||||
f.actionMu.Lock()
|
f.actionMu.Lock()
|
||||||
f.acceptActions = true
|
f.acceptActions = true
|
||||||
f.pendingActions = f.pendingActions[:0]
|
f.pendingActions = f.pendingActions[:0]
|
||||||
|
f.pendingHead = 0
|
||||||
f.actionRound.Store(uint32(f.Round))
|
f.actionRound.Store(uint32(f.Round))
|
||||||
f.actionMu.Unlock()
|
f.actionMu.Unlock()
|
||||||
}
|
}
|
||||||
@@ -52,6 +53,7 @@ func (f *FightC) closeActionWindow() {
|
|||||||
f.actionMu.Lock()
|
f.actionMu.Lock()
|
||||||
f.acceptActions = false
|
f.acceptActions = false
|
||||||
f.pendingActions = f.pendingActions[:0]
|
f.pendingActions = f.pendingActions[:0]
|
||||||
|
f.pendingHead = 0
|
||||||
f.actionRound.Store(0)
|
f.actionRound.Store(0)
|
||||||
f.actionMu.Unlock()
|
f.actionMu.Unlock()
|
||||||
|
|
||||||
@@ -73,8 +75,10 @@ func (f *FightC) submitAction(act action.BattleActionI) {
|
|||||||
f.actionMu.Unlock()
|
f.actionMu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
f.compactPendingActionsLocked()
|
||||||
replaceIndex := -1
|
replaceIndex := -1
|
||||||
for i, pending := range f.pendingActions {
|
for i := f.pendingHead; i < len(f.pendingActions); i++ {
|
||||||
|
pending := f.pendingActions[i]
|
||||||
if pending == nil || actionSlotKeyFromAction(pending) != actionSlotKeyFromAction(act) {
|
if pending == nil || actionSlotKeyFromAction(pending) != actionSlotKeyFromAction(act) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -105,15 +109,23 @@ func (f *FightC) submitAction(act action.BattleActionI) {
|
|||||||
|
|
||||||
func (f *FightC) nextAction() action.BattleActionI {
|
func (f *FightC) nextAction() action.BattleActionI {
|
||||||
f.actionMu.Lock()
|
f.actionMu.Lock()
|
||||||
if len(f.pendingActions) == 0 {
|
if f.pendingHead >= len(f.pendingActions) {
|
||||||
|
f.pendingActions = f.pendingActions[:0]
|
||||||
|
f.pendingHead = 0
|
||||||
f.actionMu.Unlock()
|
f.actionMu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
act := f.pendingActions[0]
|
act := f.pendingActions[f.pendingHead]
|
||||||
copy(f.pendingActions, f.pendingActions[1:])
|
f.pendingActions[f.pendingHead] = nil
|
||||||
f.pendingActions = f.pendingActions[:len(f.pendingActions)-1]
|
f.pendingHead++
|
||||||
hasMore := len(f.pendingActions) > 0
|
hasMore := f.pendingHead < len(f.pendingActions)
|
||||||
|
if !hasMore {
|
||||||
|
f.pendingActions = f.pendingActions[:0]
|
||||||
|
f.pendingHead = 0
|
||||||
|
} else {
|
||||||
|
f.compactPendingActionsLocked()
|
||||||
|
}
|
||||||
notify := f.actionNotify
|
notify := f.actionNotify
|
||||||
f.actionMu.Unlock()
|
f.actionMu.Unlock()
|
||||||
|
|
||||||
@@ -127,6 +139,22 @@ func (f *FightC) nextAction() action.BattleActionI {
|
|||||||
return act
|
return act
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FightC) compactPendingActionsLocked() {
|
||||||
|
if f.pendingHead == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.pendingHead < len(f.pendingActions)/2 && len(f.pendingActions) < cap(f.pendingActions) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remaining := len(f.pendingActions) - f.pendingHead
|
||||||
|
copy(f.pendingActions, f.pendingActions[f.pendingHead:])
|
||||||
|
for i := remaining; i < len(f.pendingActions); i++ {
|
||||||
|
f.pendingActions[i] = nil
|
||||||
|
}
|
||||||
|
f.pendingActions = f.pendingActions[:remaining]
|
||||||
|
f.pendingHead = 0
|
||||||
|
}
|
||||||
|
|
||||||
// 玩家逃跑/无响应/掉线
|
// 玩家逃跑/无响应/掉线
|
||||||
func (f *FightC) Over(c common.PlayerI, res model.EnumBattleOverReason) {
|
func (f *FightC) Over(c common.PlayerI, res model.EnumBattleOverReason) {
|
||||||
if f.closefight {
|
if f.closefight {
|
||||||
|
|||||||
@@ -467,12 +467,7 @@ func (f *FightC) TURNOVER(cur *input.Input) {
|
|||||||
if cur == nil {
|
if cur == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, pet := range cur.BenchPets() {
|
_hasBackup = cur.HasLivingBench()
|
||||||
if pet != nil && pet.Info.Hp > 0 {
|
|
||||||
_hasBackup = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.sendLegacySpriteDie(cur, _hasBackup)
|
f.sendLegacySpriteDie(cur, _hasBackup)
|
||||||
|
|
||||||
f.Broadcast(func(ff *input.Input) {
|
f.Broadcast(func(ff *input.Input) {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type FightC struct {
|
|||||||
actionNotify chan struct{}
|
actionNotify chan struct{}
|
||||||
acceptActions bool
|
acceptActions bool
|
||||||
pendingActions []action.BattleActionI // 待处理动作队列,同一战斗位最多保留一个动作
|
pendingActions []action.BattleActionI // 待处理动作队列,同一战斗位最多保留一个动作
|
||||||
|
pendingHead int
|
||||||
actionRound atomic.Uint32
|
actionRound atomic.Uint32
|
||||||
|
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
@@ -250,20 +251,13 @@ func (f *FightC) sideHasActionableSlots(side int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) slotNeedsAction(in *input.Input) bool {
|
func (f *FightC) slotNeedsAction(in *input.Input) bool {
|
||||||
var bench []*info.BattlePetEntity
|
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if current := in.CurrentPet(); current != nil && current.Info.Hp > 0 {
|
if current := in.CurrentPet(); current != nil && current.Info.Hp > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
bench = in.BenchPets()
|
return in.HasLivingBench()
|
||||||
for _, pet := range bench {
|
|
||||||
if pet != nil && pet.Info.Hp > 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) setActionAttackValue(act action.BattleActionI) {
|
func (f *FightC) setActionAttackValue(act action.BattleActionI) {
|
||||||
|
|||||||
@@ -107,18 +107,14 @@ func (our *Input) GetProp(id int) alpacadecimal.Decimal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (our *Input) GetEffect(etype EnumEffectType, id int) Effect {
|
func (our *Input) GetEffect(etype EnumEffectType, id int) Effect {
|
||||||
var ret []Effect
|
|
||||||
pr := EffectIDCombiner{}
|
pr := EffectIDCombiner{}
|
||||||
pr.Combine(etype, 0, gconv.Uint16(id))
|
pr.Combine(etype, 0, gconv.Uint16(id))
|
||||||
|
our.ensureEffectIndex()
|
||||||
for _, v := range our.Effects {
|
bucket := our.effectsByBase[pr.Base]
|
||||||
if v.ID().Base == pr.Base && v.Alive() {
|
for i := len(bucket) - 1; i >= 0; i-- {
|
||||||
ret = append(ret, v)
|
if bucket[i] != nil && bucket[i].Alive() {
|
||||||
|
return bucket[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
if len(ret) > 0 {
|
|
||||||
return ret[len(ret)-1]
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -178,6 +174,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
|
|||||||
if e == nil {
|
if e == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
our.ensureEffectIndex()
|
||||||
ctx := e.Ctx()
|
ctx := e.Ctx()
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
if ctx.Source == nil {
|
if ctx.Source == nil {
|
||||||
@@ -204,7 +201,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
|
|||||||
//TODO 先激活
|
//TODO 先激活
|
||||||
//fmt.Println("产生回合数", e.ID(), e.Duration())
|
//fmt.Println("产生回合数", e.ID(), e.Duration())
|
||||||
// 如果已有同 ID 的效果,尝试叠加
|
// 如果已有同 ID 的效果,尝试叠加
|
||||||
for _, v := range our.Effects {
|
for _, v := range our.effectsByBase[e.ID().Base] {
|
||||||
if v == e {
|
if v == e {
|
||||||
return nil //完全相同,跳过执行
|
return nil //完全相同,跳过执行
|
||||||
}
|
}
|
||||||
@@ -219,7 +216,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
|
|||||||
if !v.CanStack() { //说明进行了替换
|
if !v.CanStack() { //说明进行了替换
|
||||||
v.Alive(false) //不允许叠层,取消效果
|
v.Alive(false) //不允许叠层,取消效果
|
||||||
e.Duration(utils.Max(e.Duration(), v.Duration()))
|
e.Duration(utils.Max(e.Duration(), v.Duration()))
|
||||||
our.Effects = append(our.Effects, e)
|
our.appendEffect(e)
|
||||||
return v //这里把V替换掉了
|
return v //这里把V替换掉了
|
||||||
} else {
|
} else {
|
||||||
//默认给叠一层
|
//默认给叠一层
|
||||||
@@ -237,7 +234,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
|
|||||||
}
|
}
|
||||||
//无限叠加,比如能力提升类buff
|
//无限叠加,比如能力提升类buff
|
||||||
// 如果没有同 ID 的效果,直接添加
|
// 如果没有同 ID 的效果,直接添加
|
||||||
our.Effects = append(our.Effects, e)
|
our.appendEffect(e)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +306,34 @@ func (our *Input) CancelTurn(in *Input) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (our *Input) ensureEffectIndex() {
|
||||||
|
if our == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if our.effectsByBase == nil {
|
||||||
|
our.effectsByBase = make(map[int64][]Effect, len(our.Effects))
|
||||||
|
}
|
||||||
|
if our.indexedEffects > len(our.Effects) {
|
||||||
|
our.effectsByBase = make(map[int64][]Effect, len(our.Effects))
|
||||||
|
our.indexedEffects = 0
|
||||||
|
}
|
||||||
|
for our.indexedEffects < len(our.Effects) {
|
||||||
|
effect := our.Effects[our.indexedEffects]
|
||||||
|
if effect != nil {
|
||||||
|
our.effectsByBase[effect.ID().Base] = append(our.effectsByBase[effect.ID().Base], effect)
|
||||||
|
}
|
||||||
|
our.indexedEffects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (our *Input) appendEffect(effect Effect) {
|
||||||
|
if our == nil || effect == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
our.Effects = append(our.Effects, effect)
|
||||||
|
our.ensureEffectIndex()
|
||||||
|
}
|
||||||
|
|
||||||
// // 消除全部 断回合效果,但是我放下场的时候应该断掉所有的回合类效果
|
// // 消除全部 断回合效果,但是我放下场的时候应该断掉所有的回合类效果
|
||||||
// func (our *Input) CancelAll() {
|
// func (our *Input) CancelAll() {
|
||||||
// our.Effects = make([]Effect, 0)
|
// our.Effects = make([]Effect, 0)
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ import (
|
|||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var statusBonuses = map[info.EnumPetStatus]float64{
|
||||||
|
info.PetStatus.Paralysis: 1.5,
|
||||||
|
info.PetStatus.Poisoned: 1.5,
|
||||||
|
info.PetStatus.Sleep: 2.0,
|
||||||
|
// /info.BattleStatus.Frozen: 2.0,
|
||||||
|
}
|
||||||
|
|
||||||
type Input struct {
|
type Input struct {
|
||||||
CanChange uint32 //是否可以死亡切换CanChange
|
CanChange uint32 //是否可以死亡切换CanChange
|
||||||
// CanAction bool //是否可以行动
|
// CanAction bool //是否可以行动
|
||||||
@@ -27,9 +34,11 @@ type Input struct {
|
|||||||
CanCapture int
|
CanCapture int
|
||||||
Finished bool //是否加载完成
|
Finished bool //是否加载完成
|
||||||
// info.BattleActionI
|
// info.BattleActionI
|
||||||
Effects []Effect //effects 实际上全局就是effect无限回合 //effects容器 技能的
|
Effects []Effect //effects 实际上全局就是effect无限回合 //effects容器 技能的
|
||||||
EffectCache []Effect //这里是命中前执行的容器,也就是命中前执行的所有逻辑相关,理论上一个effect被激活,就应该同时将其他的effect取消激活
|
EffectCache []Effect //这里是命中前执行的容器,也就是命中前执行的所有逻辑相关,理论上一个effect被激活,就应该同时将其他的effect取消激活
|
||||||
EffectLost []Effect
|
EffectLost []Effect
|
||||||
|
effectsByBase map[int64][]Effect
|
||||||
|
indexedEffects int
|
||||||
// 删掉伤害记录,可以在回调中记录,而不是每次调用记录
|
// 删掉伤害记录,可以在回调中记录,而不是每次调用记录
|
||||||
*model.AttackValue
|
*model.AttackValue
|
||||||
FightC common.FightI
|
FightC common.FightI
|
||||||
@@ -57,6 +66,7 @@ func NewInput(c common.FightI, p common.PlayerI) *Input {
|
|||||||
ret := &Input{FightC: c, Player: p}
|
ret := &Input{FightC: c, Player: p}
|
||||||
ret.Effects = make([]Effect, 0)
|
ret.Effects = make([]Effect, 0)
|
||||||
ret.CurPet = make([]*info.BattlePetEntity, 0)
|
ret.CurPet = make([]*info.BattlePetEntity, 0)
|
||||||
|
ret.effectsByBase = make(map[int64][]Effect)
|
||||||
|
|
||||||
// t := Geteffect(EffectType.Damage, 0)
|
// t := Geteffect(EffectType.Damage, 0)
|
||||||
// t.Effect.SetArgs(ret)
|
// t.Effect.SetArgs(ret)
|
||||||
@@ -115,6 +125,27 @@ func (our *Input) BenchPets() []*info.BattlePetEntity {
|
|||||||
return bench
|
return bench
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (our *Input) HasLivingBench() bool {
|
||||||
|
if our == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
current := our.CurrentPet()
|
||||||
|
currentCatchTime := uint32(0)
|
||||||
|
if current != nil {
|
||||||
|
currentCatchTime = current.Info.CatchTime
|
||||||
|
}
|
||||||
|
for _, pet := range our.AllPet {
|
||||||
|
if pet == nil || pet.Info.Hp == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current != nil && pet.Info.CatchTime == currentCatchTime {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (our *Input) OpponentSlots() []*Input {
|
func (our *Input) OpponentSlots() []*Input {
|
||||||
if our == nil {
|
if our == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -299,20 +330,12 @@ func (our *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.Chang
|
|||||||
// GetStatusBonus 获取最高的状态倍率
|
// GetStatusBonus 获取最高的状态倍率
|
||||||
// 遍历状态数组,返回存在的状态中最高的倍率(无状态则返回1.0)
|
// 遍历状态数组,返回存在的状态中最高的倍率(无状态则返回1.0)
|
||||||
func (our *Input) GetStatusBonus() float64 {
|
func (our *Input) GetStatusBonus() float64 {
|
||||||
// 异常状态倍率映射表(状态索引 -> 倍率)
|
|
||||||
var statusBonuses = map[info.EnumPetStatus]float64{
|
|
||||||
info.PetStatus.Paralysis: 1.5,
|
|
||||||
info.PetStatus.Poisoned: 1.5,
|
|
||||||
info.PetStatus.Sleep: 2.0,
|
|
||||||
// /info.BattleStatus.Frozen: 2.0,
|
|
||||||
}
|
|
||||||
maxBonus := 1.0 // 默认无状态倍率
|
maxBonus := 1.0 // 默认无状态倍率
|
||||||
|
|
||||||
for statusIdx := 0; statusIdx < 20; statusIdx++ {
|
for statusIdx := 0; statusIdx < 20; statusIdx++ {
|
||||||
t := our.InitEffect(EffectType.Status, statusIdx)
|
t := our.GetEffect(EffectType.Status, statusIdx)
|
||||||
|
|
||||||
// 检查状态是否存在(数组中值为1表示存在该状态)
|
if t != nil && t.Alive() {
|
||||||
if t != nil && t.Stack() > 0 {
|
|
||||||
if bonus, exists := statusBonuses[info.EnumPetStatus(statusIdx)]; exists && bonus > maxBonus {
|
if bonus, exists := statusBonuses[info.EnumPetStatus(statusIdx)]; exists && bonus > maxBonus {
|
||||||
maxBonus = bonus
|
maxBonus = bonus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type EffectNode struct {
|
|||||||
canStack bool // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
|
canStack bool // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
|
||||||
isFirst bool
|
isFirst bool
|
||||||
SideEffectArgs []int // 附加效果参数
|
SideEffectArgs []int // 附加效果参数
|
||||||
|
cachedArgs []alpacadecimal.Decimal
|
||||||
// owner bool //是否作用自身
|
// owner bool //是否作用自身
|
||||||
Success bool // 是否执行成功 成功XXX,失败XXX
|
Success bool // 是否执行成功 成功XXX,失败XXX
|
||||||
arget bool // 传出作用对象,默认0是自身,1是作用于对面
|
arget bool // 传出作用对象,默认0是自身,1是作用于对面
|
||||||
@@ -240,18 +241,22 @@ func (e *EffectNode) SetArgs(t *input.Input, a ...int) {
|
|||||||
e.Input = t
|
e.Input = t
|
||||||
if len(a) > 0 {
|
if len(a) > 0 {
|
||||||
e.SideEffectArgs = a
|
e.SideEffectArgs = a
|
||||||
|
e.cachedArgs = e.cachedArgs[:0]
|
||||||
|
for _, v := range a {
|
||||||
|
e.cachedArgs = append(e.cachedArgs, alpacadecimal.NewFromInt(int64(v)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
func (e *EffectNode) Args() []alpacadecimal.Decimal {
|
func (e *EffectNode) Args() []alpacadecimal.Decimal {
|
||||||
var ret []alpacadecimal.Decimal
|
if len(e.cachedArgs) == len(e.SideEffectArgs) {
|
||||||
|
return e.cachedArgs
|
||||||
for _, v := range e.SideEffectArgs {
|
|
||||||
ret = append(ret, alpacadecimal.NewFromInt(int64(v)))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
e.cachedArgs = e.cachedArgs[:0]
|
||||||
return ret
|
for _, v := range e.SideEffectArgs {
|
||||||
|
e.cachedArgs = append(e.cachedArgs, alpacadecimal.NewFromInt(int64(v)))
|
||||||
|
}
|
||||||
|
return e.cachedArgs
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user