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